Last active
February 26, 2019 20:27
-
-
Save adamchel/96bf0d6da268ea252f078a40fce523ec to your computer and use it in GitHub Desktop.
Small sample application demonstrating custom Codecs with MongoDB Stitch TypeScript SDK
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
AnonymousCredential, | |
BSON, | |
Codec, | |
Stitch, | |
RemoteMongoClient, | |
RemoteMongoCollection | |
} from 'mongodb-stitch-server-sdk'; | |
// This is the interface we want to insert and retrieve from the collection. | |
interface CustomObject { | |
_id: BSON.ObjectId, | |
owner_id: string, | |
message: string, | |
mixed_type: string | number, | |
} | |
// This is the codec that should be passed to the Stitch SDK to handle | |
// the conversion of BSON documents from the database into objects that | |
// match the interface. CustomObject could also be a class instead of an | |
// interface, if you wanted to have methods on the objects that get inserted | |
// into and retrieved from the database. | |
class CustomObjectCodec implements Codec<CustomObject> { | |
// This is a user-defined type guard where you can verify that the | |
// object returned from MongoDB is valid. | |
// see: https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards | |
isCustomObject(obj: any): obj is CustomObject { | |
return obj._id !== undefined && obj._id instanceof BSON.ObjectID | |
&& obj.owner_id !== undefined && (typeof obj.owner_id === "string") | |
&& obj.message !== undefined && (typeof obj.message === "string") | |
&& obj.mixed_type !== undefined | |
&& ((typeof obj.mixed_type === "string") || typeof obj.mixed_type === "number"); | |
} | |
// Decodes a document/object from the collection into a CustomObject. This is | |
// where you can implement any advanced decoding logic you might need. For this | |
// sample, we just verify that the input document has the correct types. | |
decode(from: any): CustomObject { | |
if(this.isCustomObject(from)) { | |
return from; | |
} | |
// the check is not strictly necessary, but it lets you throw a | |
// customized error if the document you get back from the database does | |
// not match what you were expecting, instead of getting an unexpected | |
// `undefined` elsewhere in your code | |
throw new Error("object not valid") | |
} | |
// Encodes the provided CustomObject into a plain JS object before it gets. | |
// inserted into the MongoDB database. This is also where you can modify | |
// any fields before they get inserted into the database. | |
encode(from: CustomObject): object { | |
return { | |
_id: from._id, | |
owner_id: from.owner_id, | |
message: from.message, | |
mixed_type: from.mixed_type | |
} | |
} | |
} | |
// This is just a class that implements CustomObject, so we can demonstrate how | |
// you can actually insert not just interfaces, but also class instances. | |
class ConcreteCustomObject implements CustomObject { | |
_id: BSON.ObjectId; | |
owner_id: string; | |
message: string; | |
mixed_type: string | number; | |
constructor(owner_id: string, message: string, mixed_type: string | number) { | |
this._id = new BSON.ObjectId(); | |
this.owner_id = owner_id; | |
this.message = message; | |
this.mixed_type = mixed_type; | |
} | |
} | |
// This logs into the Stitch app, and demonstrates using a CustomObject | |
// collection, and getting back a CustomObject from a Stitch function. This | |
// requires a Stitch app with a rule on `custom.items`, and a Stitch function | |
// called `getCustomObject` which returns an object that can be casted to | |
// a TypeScript interface. | |
const client = Stitch.initializeDefaultAppClient('your-client-app-id'); | |
client.auth.loginWithCredential(new AnonymousCredential()).then(user => { | |
console.log(user.id); | |
demonstrateCollectionWithCustomType() | |
demonstrateFunctionWithCustomType() | |
client.close(); | |
}).catch((err: any) => { | |
console.log(err); | |
client.close(); | |
}) | |
function demonstrateCollectionWithCustomType() { | |
let mongoClient = client.getServiceClient( | |
RemoteMongoClient.factory, "mongodb-atlas" | |
); | |
// create a collection that uses the CustomObjectCodec | |
// to encode and decode from the collection | |
let coll: RemoteMongoCollection<CustomObject> = mongoClient | |
.db("custom") | |
.collection("items", new CustomObjectCodec()); | |
let customObjToInsert1: CustomObject = new ConcreteCustomObject( | |
client.auth.user!.id, | |
"this is a JS class", | |
42 | |
); | |
let customObjToInsert2: CustomObject = new ConcreteCustomObject( | |
client.auth.user!.id, | |
"this is also a JS class", | |
"43" | |
); | |
// we can insert the custom objects directly into the collection | |
coll.insertMany([customObjToInsert1, customObjToInsert2]).then(() => { | |
// and then we can read `CustomObject`s directly from the collection | |
return coll.find().first() | |
}).then(thisIsACustomObj => { | |
if (thisIsACustomObj === undefined) { | |
throw new Error("could not find any objects"); | |
} | |
// now we can access this as if it were a `CustomObject` | |
console.log(thisIsACustomObj._id); | |
console.log(thisIsACustomObj.message); | |
console.log(thisIsACustomObj.mixed_type); | |
console.log(thisIsACustomObj.owner_id); | |
}); | |
} | |
function demonstrateFunctionWithCustomType() { | |
// NOTE: this is just a cast, not actually a conversion using the | |
// Codec. this means that if the function returns an invalid CustomObject, | |
// then you might get errors elsewhere in the code. We may add support for | |
// Codecs for callFunction in the future. Let us know if it's something you | |
// would find useful! | |
client.callFunction("getCustomObj", []).then((customObjResult: CustomObject) => { | |
// now we can access this as if it were a `CustomObject` | |
console.log(customObjResult._id); | |
console.log(customObjResult.message); | |
console.log(customObjResult.mixed_type); | |
console.log(customObjResult.owner_id); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment