REST AP Is And Mongoose
Mongoose
Mongoose is a library that makes managing MongoDB easier. In particular, it allows us to create models or schemas for our data/documents. This means:
We can define what data types each field in a document will accept
Which fields are required
And more
Note: Mongoose is known as an object document mapper or ODM: it maps objects in Node.js to documents in MongoDB.
Setting up Mongoose
Here's basic setup to start using Mongoose:
npm install mongoose
.const mongoose = require('mongoose')
.Connect to database with database name as path:
mongoose.connect('mongodb://127.0.0.1:27017/dbName', { options })
.Create model:
const User = mongoose.model('User', { fieldsConfig })
.Create model instance:
const instance = new User({ fields })
.Do stuff to the database using instance:
instance.save().then(user => console.log(user))
creates a document.
Here's more details:
Things to note:
The callback in
then
returns the document created, which will be identical tome
.If you provide the wrong data types, the
catch
method will run its callback!In the database, Mongoose creates a field called
_v
, which represents the version of the document.
Data validation and sanitization
Data validation is about forcing data to conform to some rules (like requiring a user's age to be 18 or older). Data sanitization is about altering and cleaning up the data before saving it (like removing whitespace around a name).
Built-in validators include:
type
defines the data typerequired
min
andmax
for numbersenum
,match
,minlength
, andmaxlength
for strings
In order to create a custom validator, you just provide a validate
method that throws an error if your logic tests fail:
Pro tip: For more complex validators, you can download the validator
npm package.
Built-in sanitizers include:
default
sets a default value if one isn't providedlowercase
,uppercase
, andtrim
tell Mongoose to runtoLowerCase()
,toUpperCase()
, andtrim()
Structuring a REST API
What is representational state transfer?
Representational state transfer allows clients to access and manipulate resources/data using pre-defined operations. It's representational because the resources/data live on the database/server, and it sends a representation of those resources/data to the client.
CRUD resources
When exposing a resource via pre-defined operations, you usually create a set of CRUD endpoints of this form:
Create
POST /tasks (creates new task)
Read
GET /tasks (gets all tasks)
GET /tasks/:id (gets one task)
Update
PATCH /tasks/:id (updates one task)
Delete
DELETE /tasks/:id (deletes on task)
Structure of HTTP requests and responses
HTTP requests have the following structure:
Method, path, protocol
An arbitrary number of request headers used to attach metadata
Accept
defines what the client expects backConnection
defines how long to keep a connection aliveAuthorization
is used for authenticationEtc.
Request body containing information for processing
HTTP responses have a similar structure:
Protocol, status code, text representation of status
Response headers for metadata
Date
tells you the time that the operation occurredServer
tells you what server it's coming fromContent-Type
tells you what kind of data is being returnedEtc.
Response body
Resource Endpoints
We use clear status codes to communicate meaning.
When you provide a string
id
to Mongoose, it will convert it to anObjectId
for you.
Async/Await
When you define a function as async
, its invocation returns a promise with
The returned value as the resolved value or
Any thrown error or rejected promise's value as the rejected value.
An extremely valuable bonus to async
is that it provides the await
operator inside of the function. await
is basically syntactic sugar where code can look like it's running synchronously, making it easier to read. You can even store the resolved value in a variable.
Note: If one of your await
functions gets rejected, the async
function exits, and you can catch the rejected value.
Pro tip: A huge benefit of await
is also the fact that your asynchronous calls are all in the same scope. That means that if you need to share values or variables, it's easy.
Common patterns
With all of this information in mind, async
functions form a coding pattern where you package together multiple asynchronous operations and use a then
and catch
to handle all of them together:
Another pattern you can utilize is the try/catch
block inside the async
function. This is useful when you don't care about what is returned by the async
function--like in a callback function!
Last updated