# data[].attributes.body
Is it better to set up one Schema for combined User & Item entry

OR Is it better to set up two Schemas - one for Users and one for Items (which would create two collections, I think) and then link them "somehow" via the UserName.

We ask because Users and Items will have different operations in that the User can add/edit/delete their Items, and can also Create/Edit/Delete their accounts.

# data[].attributes.body
Step-by-step instructions for connecting Mongoose to MongoDB Atlas

# data[].attributes.attachment_link.readable_text
We will not be installing MongoDB directly on our machines. Instead, we will be connecting to remote collections using the MongoDB Atlas cloud hosting platform.

1. Create an free account with MongoDB Atlas. You will be prompted to set up your account (the Atlas interface will point you in the right direction):

2. Screen 1: The basics

Organization: SAIT WBDV (or whatever)

Project Name: Animals (or whatever)

Preferred Language: JavaScript (obvs)

3. Screen 3: Create a Shared Cluster

Cloud Provider: This doesn't really matter but Tony uses AWS.

Region: Tony uses Oregon (it's closest).

After you submit, it'll take awhile for the server to set up your Cluster.

When it's done:

Your Express app will need a login to access any databases you set up. You will need your login information later for your connection string.

1. From the Atlas Admin panel, click Database Access (under "Security" on the left-hand side).

2. Add New Database User

Authentication Method: Password

Username: something you'll remember.

Password: Do NOT use a password you use regularly; it's too easy to accidentally push it to a repo.

Database User Privileges: Read and write to any database (avoid Atlas admin unless you know you need it)

1. Click Network Access (under Database Access)

2. Add IP Address

3. Allow Access from Anywhere
Caveat: This is inherently insecure but is good enough for class work. Do NOT add any sensitive information to any databases to this cluster; Tony will find it and profit off your hard work.

These are datasets that are more complex than we need for this course but they are a nice resource if you want to dip into heavier demo projects.

The Connection String holds all the information your application needs to connect to your database. This will be stored in your .env file (along with your PORT number).

1. Click on Clusters (under "Data Storage" in the left-hand menu)

2. Under Clusters, click Connect.

3. Connect your application

4. Copy the Connection String (starting with mongodb+srv://) into an .env file like so:

Your connection string will have real values, except for:

<password>. Paste your DB password (created in Step 2.2) here.

Change myFirstDatabase to myProject, favouriteThings or similar.

It's important that you use MONGODB_URL as a variable name; this is what Heroku uses to connect to Atlas (that's tomorrow).

1. Clone or copy this gist to your machine.

2. Add your .env file (with the connection string you created above) to the root directory.

3. Navigation to your root directory on the command line.

The 'uri' parameter to 'openUri()' must be a string, got "undefined"
You probably don't have a .env file or it's not loading correctly.

The main error you will run into when connecting is Authentication Failed. Try the following:

Confirm you've set the correct username/password in your connection string. You can reset the password in the Atlas control panel.

Confirm you've changed the database in your connection string to myProject or similar.

Confirm you've whitelisted all IPs in Atlas.

# data[].attributes.attachment_link.title
MongoDB Atlas: Deploying a collection to the cloud

# data[].attributes.attachment_link.description
We will not be installing MongoDB directly on our machines. Instead, we will be connecting to remote collections using the MongoDB Atlas cloud hosting platform. Create an free account with MongoDB Atlas.

# data[].attributes.headline
Seeding a Mongoose collection with initial data

# data[].attributes.subject
Seeding a Mongoose collection with initial data

# data[].attributes.body
There are many ways to do this but we'll probably just create a JSON file from the superhero data and import using Compass.

# data[].attributes.body
The following document outlines Create operations. Scroll to the Related Gists section for Read, Update and Delete

# data[].attributes.attachment_link.title
Mongoose: Create MongoDB Document from form data in Express

# data[].attributes.attachment_link.description
Mongoose: Create MongoDB Document from form data in Express - README.md

# data[].attributes.body
A sample Postman collection for testing endpoint response codes.

# data[].attributes.attachment_link.readable_text
Instantly share code, notes, and snippets.

Postman: Endpoint health checks

You can't perform that action at this time.

You signed in with another tab or window. You signed out in another tab or window. Reload to refresh your session. Reload to refresh your session.

# data[].attributes.attachment_link.description
Instantly share code, notes, and snippets. Postman: Endpoint health checks You can't perform that action at this time. You signed in with another tab or window. You signed out in another tab or window. Reload to refresh your session. Reload to refresh your session.

# data[].attributes.body
Headers, methods, status codes, etc

See C6 Tech 9 Slides

# data[].attributes.body
Common terminology and key takeaways when dealing with REST APIs

See C6 Tech 9 Slides

# data[].attributes.attachment_link.readable_text
Endpoint

A fancy URL. Also referred to as a path.

Endpoint segment

The bits of a URL located between the slashes / in a URL. In GET /posts/latest/comments: posts, latest and comments are endpoint segments.

Resource

The a file or service that's available at an endpoint.

HTTP Verb

The Web implementation of CRUD operations: Create (POST), Read (GET), Update (PUT), Delete (DELETE).

Route

An HTTP Verb paired with a Resource (the noun). Together, a route provides you with the resource you'd like to access and what you'd like to do with it.

Route parameter

A named endpoint segment, used to capture values from a URL. Example: GET /users/:id

Search/URL/Query parameter

The name/value pairs that are included after the ? in a URL. Example: GET /users?sort=asc where sort is the name and asc is the value.

# data[].attributes.attachment_link.description
Endpoint A fancy URL. Also referred to as a path. Endpoint segment The bits of a URL located between the slashes / in a URL. In GET /posts/latest/comments: posts, latest and comments are endpoint segments. Resource The a file or service that's available at an endpoint.

# data[].attributes.body
A great article outline some (opinionated) conventions and best practices to keep in mind when designing your REST API.

# data[].attributes.attachment_link.readable_text
REST APIs are one of the most common kinds of web services available today. They allow various clients including browser apps to communicate with a server via the REST API. Therefore, it's very important to design REST APIs properly so that we won't run into problems down the road. We have to take into account security, performance, and ease of use for API consumers.

Otherwise, we create problems for clients that use our APIs, which isn't pleasant and detracts people from using our API. If we don't follow commonly accepted conventions, then we confuse the maintainers of the API and the clients that use them since it's different from what everyone expects.

In this article, we'll look at how to design REST APIs to be easy to understand for anyone consuming them, future-proof, and secure and fast since they serve data to clients that may be confidential.

What is a REST API?

A REST API is an application programming interface that conforms to specific architectural constraints, like stateless communication and cacheable data. It is not a protocol or standard. While REST APIs can be accessed through a number of communication protocols, most commonly, they are called over HTTPS, so the guidelines below

Accept and respond with JSON

REST APIs should accept JSON for request payload and also send responses to JSON. JSON is the standard for transferring data. Almost every networked technology can use it: JavaScript has built-in methods to encode and decode JSON either through the Fetch API or another HTTP client. Server-side technologies have libraries that can decode JSON without doing much work.

There are other ways to transfer data. XML isn't widely supported by frameworks without transforming the data ourselves to something that can be used, and that's usually JSON. We can't manipulate this data as easily on the client-side, especially in browsers. It ends up being a lot of extra work just to do normal data transfer.

Form data is good for sending data, especially if we want to send files. But for text and numbers, we don't need form data to transfer those since-with most frameworks-we can transfer JSON by just getting the data from it directly on the client side. It's by far the most straightforward to do so.

To make sure that when our REST API app responds with JSON that clients interpret it as such, we should set Content-Type in the response header to application/json after the request is made. Many server-side app frameworks set the response header automatically. Some HTTP clients look at the Content-Type response header and parse the data according to that format.

The only exception is if we're trying to send and receive files between client and server. Then we need to handle file responses and send form data from client to server. But that is a topic for another time.

We should also make sure that our endpoints return JSON as a response. Many server-side frameworks have this as a built-in feature.

Let's take a look at an example API that accepts JSON payloads. This example will use the Express back end framework for Node.js. We can use the body-parser middleware to parse the JSON request body, and then we can call the res.json method with the object that we want to return as the JSON response as follows:

const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); app.post('/', (req, res) => {   res.json(req.body); }); app.listen(3000, () => console.log('server started'));

Set the Content-Type header in the response to application/json; charset=utf-8 without any changes. The method above applies to most other back end frameworks.

Use nouns instead of verbs in endpoint paths

We shouldn't use verbs in our endpoint paths. Instead, we should use the nouns which represent the entity that the endpoint that we're retrieving or manipulating as the pathname.

This is because our HTTP request method already has the verb. Having verbs in our API endpoint paths isn't useful and it makes it unnecessarily long since it doesn't convey any new information. The chosen verbs could vary by the developer's whim. For instance, some like 'get' and some like 'retrieve', so it's just better to let the HTTP GET verb tell us what and endpoint does.

The action should be indicated by the HTTP request method that we're making. The most common methods include GET, POST, PUT, and DELETE.

GET retrieves resources.

POST submits new data to the server.

PUT updates existing data.

DELETE removes data.

The verbs map to CRUD operations.

With the two principles we discussed above in mind, we should create routes like GET /articles/ for getting news articles. Likewise, POST /articles/ is for adding a new article , PUT /articles/:id is for updating the article with the given id. DELETE /articles/:id is for deleting an existing article with the given ID.

/articles represents a REST API resource. For instance, we can use Express to add the following endpoints for manipulate articles as follows:

const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); app.get('/articles', (req, res) => {   const articles = [];   // code to retrieve an article...   res.json(articles); }); app.post('/articles', (req, res) => {   // code to add a new article...   res.json(req.body); }); app.put('/articles/:id', (req, res) => {   const { id } = req.params;   // code to update an article...   res.json(req.body); }); app.delete('/articles/:id', (req, res) => {   const { id } = req.params;   // code to delete an article...   res.json({ deleted: id }); }); app.listen(3000, () => console.log('server started'));

In the code above, we defined the endpoints to manipulate articles. As we can see, the path names do not have any verbs in them. All we have are nouns. The verbs are in the HTTP verbs.

The POST, PUT, and DELETE endpoints all take JSON as the request body, and they all return JSON as the response, including the GET endpoint.

Use logical nesting on endpoints

When designing endpoints, it makes sense to group those that contain associated information. That is, if one object can contain another object, you should design the endpoint to reflect that. This is good practice regardless of whether your data is structured like this in your database. In fact, it may be advisable to avoid mirroring your database structure in your endpoints to avoid giving attackers unnecessary information.

const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); app.get('/articles/:articleId/comments', (req, res) => {   const { articleId } = req.params;   const comments = [];   // code to get comments by articleId   res.json(comments); }); app.listen(3000, () => console.log('server started'));

In the code above, we can use the GET method on the path '/articles/:articleId/comments'. We get comments on the article identified by articleId and then return it in the response. We add 'comments' after the '/articles/:articleId' path segment to indicate that it's a child resource of /articles.

This makes sense since comments are the children objects of the articles, assuming each article has its own comments. Otherwise, it's confusing to the user since this structure is generally accepted to be for accessing child objects. The same principle also applies to the POST, PUT, and DELETE endpoints. They can all use the same kind of nesting structure for the path names.

However, nesting can go too far. After about the second or third level, nested endpoints can get unwieldy. Consider, instead, returning the URL to those resources instead, especially if that data is not necessarily contained within the top level object.

For example, suppose you wanted to return the author of particular comments. You could use /articles/:articleId/comments/:commentId/author. But that's getting out of hand. Instead, return the URI for that particular user within the JSON response instead:

Handle errors gracefully and return standard error codes

To eliminate confusion for API users when an error occurs, we should handle errors gracefully and return HTTP response codes that indicate what kind of error occurred. This gives maintainers of the API enough information to understand the problem that's occurred. We don't want errors to bring down our system, so we can leave them unhandled, which means that the API consumer has to handle them.

Common error HTTP status codes include:

400 Bad Request - This means that client-side input fails validation.

401 Unauthorized - This means the user isn't not authorized to access a resource. It usually returns when the user isn't authenticated.

403 Forbidden - This means the user is authenticated, but it's not allowed to access a resource.

404 Not Found - This indicates that a resource is not found.

500 Internal server error - This is a generic server error. It probably shouldn't be thrown explicitly.

502 Bad Gateway - This indicates an invalid response from an upstream server.

503 Service Unavailable - This indicates that something unexpected happened on server side (It can be anything like server overload, some parts of the system failed, etc.).

We should be throwing errors that correspond to the problem that our app has encountered. For example, if we want to reject the data from the request payload, then we should return a 400 response as follows in an Express API:

const express = require('express'); const bodyParser = require('body-parser'); const app = express(); // existing users const users = [   { email: 'abc@foo.com' } ] app.use(bodyParser.json()); app.post('/users', (req, res) => {   const { email } = req.body;   const userExists = users.find(u => u.email === email);   if (userExists) {     return res.status(400).json({ error: 'User already exists' })   }   res.json(req.body); }); app.listen(3000, () => console.log('server started'));

In the code above, we have a list of existing users in the users array with the given email.

Then if we try to submit the payload with the email value that already exists in users, we'll get a 400 response status code with a 'User already exists' message to let users know that the user already exists. With that information, the user can correct the action by changing the email to something that doesn't exist.

Error codes need to have messages accompanied with them so that the maintainers have enough information to troubleshoot the issue, but attackers can't use the error content to carry our attacks like stealing information or bringing down the system.

Whenever our API does not successfully complete, we should fail gracefully by sending an error with information to help users make corrective action.

Allow filtering, sorting, and pagination

The databases behind a REST API can get very large. Sometimes, there's so much data that it shouldn't be returned all at once because it's way too slow or will bring down our systems. Therefore, we need ways to filter items.

We also need ways to paginate data so that we only return a few results at a time. We don't want to tie up resources for too long by trying to get all the requested data at once.

Filtering and pagination both increase performance by reducing the usage of server resources. As more data accumulates in the database, the more important these features become.

Here's a small example where an API can accept a query string with various query parameters to let us filter out items by their fields:

const express = require('express'); const bodyParser = require('body-parser'); const app = express(); // employees data in a database const employees = [   { firstName: 'Jane', lastName: 'Smith', age: 20 },   //...   { firstName: 'John', lastName: 'Smith', age: 30 },   { firstName: 'Mary', lastName: 'Green', age: 50 }, ] app.use(bodyParser.json()); app.get('/employees', (req, res) => {   const { firstName, lastName, age } = req.query;   let results = [...employees];   if (firstName) {     results = results.filter(r => r.firstName === firstName);   }   if (lastName) {     results = results.filter(r => r.lastName === lastName);   }   if (age) {     results = results.filter(r => +r.age === +age);   }   res.json(results); }); app.listen(3000, () => console.log('server started'));

Once we have done that, we return the results as the response. Therefore, when we make a GET request to the following path with the query string:

/employees?lastName=Smith&age=30
[     {         "firstName": "John",         "lastName": "Smith",         "age": 30     } ]

Likewise, we can accept the page query parameter and return a group of entries in the position from (page - 1) * 20 to page * 20.

We can also specify the fields to sort by in the query string. For instance, we can get the parameter from a query string with the fields we want to sort the data for. Then we can sort them by those individual fields.

For instance, we may want to extract the query string from a URL like:

http://example.com/articles?sort=+author,-datepublished
Where + means ascending and - means descending. So we sort by author's name in alphabetical order and datepublished from most recent to least recent.

Maintain good security practices

Most communication between client and server should be private since we often send and receive private information. Therefore, using SSL/TLS for security is a must.

A SSL certificate isn't too difficult to load onto a server and the cost is free or very low. There's no reason not to make our REST APIs communicate over secure channels instead of in the open.

People shouldn't be able to access more information that they requested. For example, a normal user shouldn't be able to access information of another user. They also shouldn't be able to access data of admins.

To enforce the principle of least privilege, we need to add role checks either for a single role, or have more granular roles for each user.

If we choose to group users into a few roles, then the roles should have the permissions that cover all they need and no more. If we have more granular permissions for each feature that users have access to, then we have to make sure that admins can add and remove those features from each user accordingly. Also, we need to add some preset roles that can be applied to a group users so that we don't have to do that for every user manually.

Cache data to improve performance

We can add caching to return data from the local memory cache instead of querying the database to get the data every time we want to retrieve some data that users request. The good thing about caching is that users can get data faster. However, the data that users get may be outdated. This may also lead to issues when debugging in production environments when something goes wrong as we keep seeing old data.

There are many kinds of caching solutions like Redis, in-memory caching, and more. We can change the way data is cached as our needs change.

For instance, Express has the middleware to add caching to our app without much configuration. We can add a simple in-memory cache into our server like so:

const express = require('express'); const bodyParser = require('body-parser'); const apicache = require('apicache'); const app = express(); let cache = apicache.middleware; app.use(cache('5 minutes')); // employees data in a database const employees = [   { firstName: 'Jane', lastName: 'Smith', age: 20 },   //...   { firstName: 'John', lastName: 'Smith', age: 30 },   { firstName: 'Mary', lastName: 'Green', age: 50 }, ] app.use(bodyParser.json()); app.get('/employees', (req, res) => {   res.json(employees); }); app.listen(3000, () => console.log('server started'));

to apply the caching to the whole app. We cache the results for five minutes, for example. We can adjust this for our needs.

If you are using caching, you should also include Cache-Control information in your headers. This will help users effectively use your caching system.

Versioning our APIs

We should have different versions of API if we're making any changes to them that may break clients. The versioning can be done according to semantic version (for example, 2.0.6 to indicate major version 2 and the sixth patch) like most apps do nowadays.

This way, we can gradually phase out old endpoints instead of forcing everyone to move to the new API at the same time. The v1 endpoint can stay active for people who don't want to change, while the v2, with its shiny new features, can serve those who are ready to upgrade. This is especially important if our API is public. We should version them so that we won't break third party apps that use our APIs.

For example, we can do that with Express as follows:

const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); app.get('/v1/employees', (req, res) => {   const employees = [];   // code to get employees   res.json(employees); }); app.get('/v2/employees', (req, res) => {   const employees = [];   // different code to get employees   res.json(employees); }); app.listen(3000, () => console.log('server started'));

We just add the version number to the start of the endpoint URL path to version them.

Conclusion

The most important takeaways for designing high-quality REST APIs is to have consistency by following web standards and conventions. JSON, SSL/TLS, and HTTP status codes are all standard building blocks of the modern web.

Performance is also an important consideration. We can increase it by not returning too much data at once. Also, we can use caching so that we don't have to query for data all the time.

Paths of endpoints should be consistent, we use nouns only since the HTTP methods indicate the action we want to take. Paths of nested resources should come after the path of the parent resource. They should tell us what we're getting or manipulating without the need to read extra documentation to understand what it's doing.

Tags: express, javascript, rest api, stackoverflow

# data[].attributes.attachment_link.title
Best practices for REST API design - Stack Overflow Blog

# data[].attributes.attachment_link.description
Learn how to design REST APIs to be easy to understand for anyone, future-proof, secure, and fast since they serve data to clients that may be confidential.

# data[].attributes.body
The Mongoose documentation has a great Getting Started tutorial if you're fuzzy on Mongoose.

# data[].attributes.attachment_link.readable_text
Getting Started
First be sure you have MongoDB and Node.js installed.
Next install Mongoose from the command line using npm:

$ npm install mongoose --save

Now say we like fuzzy kittens and want to record every kitten we ever meet in MongoDB. The first thing we need to do is include mongoose in our project and open a connection to the test database on our locally running instance of MongoDB.

 const mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true, useUnifiedTopology: true});

We have a pending connection to the test database running on localhost. We now need to get notified if we connect successfully or if a connection error occurs:

const db = mongoose.connection; db.on('error', console.error.bind(console, 'connection error:')); db.once('open', { });

Once our connection opens, our callback will be called. For brevity, let's assume that all following code is within this callback.

With Mongoose, everything is derived from a Schema. Let's get a reference to it and define our kittens.

So far so good. We've got a schema with one property, name, which will be a String. The next step is compiling our schema into a Model.

const Kitten = mongoose.model('Kitten', kittySchema);

A model is a class with which we construct documents. In this case, each document will be a kitten with properties and behaviors as declared in our schema. Let's create a kitten document representing the little guy we just met on the sidewalk outside:

Kittens can meow, so let's take a look at how to add "speak" functionality to our documents:

 kittySchema.methods.speak = { const greeting = this.name ? "Meow name is " + this.name : "I don't have a name"; console.log(greeting); } const Kitten = mongoose.model('Kitten', kittySchema);

Functions added to the methods property of a schema get compiled into the Model prototype and exposed on each document instance:

We have talking kittens! But we still haven't saved anything to MongoDB. Each document can be saved to the database by calling its save method. The first argument to the callback will be an error if any occurred.

Say time goes by and we want to display all the kittens we've seen. We can access all of the kitten documents through our Kitten model.

We just logged all of the kittens in our db to the console. If we want to filter our kittens by name, Mongoose supports MongoDBs rich querying syntax.

This performs a search for all documents with a name property that begins with "fluff" and returns the result as an array of kittens to the callback.

Congratulations

That's the end of our quick start. We created a schema, added a custom document method, saved and queried kittens in MongoDB using Mongoose. Head over to the guide, or API docs for more.

# data[].attributes.attachment_link.description
First be sure you have MongoDB and Node.js installed. Next install Mongoose from the command line using npm: $ npm install mongoose --save Now say we like fuzzy kittens and want to record every kitten we ever meet in MongoDB.