Skip to content

Commit 083bfd6

Browse files
committed
update module1 r3.4 to 4.2
1 parent 024a43c commit 083bfd6

File tree

4 files changed

+88
-13
lines changed
  • module1-introduction-to-backend

4 files changed

+88
-13
lines changed

module1-introduction-to-backend/r3.4-api-best-practices/README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ In general, an effective API design will have the following characteristics:
2222

2323
Please note: This list is not necessarily exhaustive and there is always room for flexibility around these principles depending on your approach towards API design.
2424

25-
1. **Accept and respond with JSON**
25+
### 1. Accept and respond with JSON
2626
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.
2727

2828
In Express, we can use the body-parser middleware to parse the JSON request body.
@@ -41,7 +41,7 @@ app.post('/', (req, res) => {
4141
app.listen(3000, () => console.log('server started'));
4242
```
4343

44-
2. **Use nouns instead of verbs in endpoint paths**
44+
### 2. Use nouns instead of verbs in endpoint paths
4545
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.
4646

4747
For example, 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. Here `articles` represents a REST API resource.
@@ -83,7 +83,7 @@ app.delete('/articles/:id', (req, res) => {
8383
app.listen(3000, () => console.log('server started'));
8484
```
8585

86-
3. **Use logical nesting on endpoints**
86+
### 3. Use logical nesting on endpoints
8787
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.
8888

8989
For example, if we want an endpoint to get the comments for a news article, we should append the `/comments` path to the end of the `/articles` path.
@@ -108,16 +108,16 @@ app.get('/articles/:articleId/comments', (req, res) => {
108108
app.listen(3000, () => console.log('server started'));
109109
```
110110

111-
4. **Handle complexity elegantly**
111+
### 4. Handle complexity elegantly
112112
The data you’re trying to expose can be characterized by a lot of properties which could be beneficial for the end consumer working with your API. These properties describe the base resource and isolate specific assets of information that can be manipulated with the appropriate method. An API should strive towards completion, and provide all the required information, data and resources to help developers integrate with them in a seamless manner.
113113

114114
However, instead of defining more resources and endpoints to cover dynamic use cases and relationships, you can sweep properties and limit responses behind the ‘?’ in a query parameter, or isolate specific component of the data the client is working with using a path parameter.
115115

116-
For example, let's consider a photosharing app. It could be of use to developers to get information on all the photos shared in a particular location and a specific hashtag. You also want to limit the number of results to 10 per API call to prevent server load. If the end user wants to find all photos in Boston with a hashtag #winter, the call would be: `GET /photos?location=boston&hashtag=winter&limit=10`. Here `location`, `hashtag` and `limit` are query parameters. However, if the end user wants to find a specific photo by it's ID, the call would be: `GET /photos/13214` where the ID 13214 is a path parameter.
116+
For example, let's consider a photosharing app. It could be of use to developers to get information on all the photos shared in a particular location and a specific hashtag. You also want to limit the number of results to 10 per API call to prevent server load. If the end user wants to find all photos in Boston with a hashtag #winter, the call would be: `GET /photos?location=boston&hashtag=winter&limit=10`. Here `location`, `hashtag` and `limit` are query parameters. However, if the end user wants to find a specific photo by its ID, the call would be: `GET /photos/13214` where the ID 13214 is a path parameter.
117117

118118
The general thumb rule is: If you want to identify a resource, you should use a path parameter. But if you want sort or filter items, then you should use one or more query parameters.
119119

120-
5. **Handle errors gracefully and return standard error codes**
120+
### 5. Handle errors gracefully and return standard error codes
121121
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 helps the frontend developers using our API handle the error situations gracefully on the UI of the application, which ultimately leads to a good user experience for the end users. In the previous lessons, we have already mentioned the most commonly used HTTP response status codes.
122122

123123
Along with error codes, try to provide good feedback through your response messages. Good feedback involves positive validation on correct implementation, and an informative error on incorrect implementation that can help users debug and correct the way they use the product. Describe your error responses well, but keep them concise and neat.
@@ -149,7 +149,7 @@ app.post('/users', (req, res) => {
149149
app.listen(3000, () => console.log('server started'));
150150
```
151151

152-
6. **Allow filtering, sorting, and pagination**
152+
### 6. Allow filtering, sorting, and pagination
153153
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.
154154

155155
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:
@@ -181,27 +181,27 @@ app.get('/employees', (req, res) => {
181181
}
182182

183183
if (age) {
184-
results = results.filter(r => +r.age === +age);
184+
results = results.filter(r => r.age > age);
185185
}
186186
res.json(results);
187187
});
188188

189189
app.listen(3000, () => console.log('server started'));
190190
```
191191

192-
7. **Maintain good security practices**
192+
### 7. Maintain good security practices
193193
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.
194194

195195
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](https://en.wikipedia.org/wiki/Principle_of_least_privilege), we need to add role checks either for a single role, or have more granular roles for each user.
196196

197197
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.
198198

199-
8. **Cache data to improve performance**
199+
### 8. Cache data to improve performance
200200
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.
201201

202202
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.
203203

204-
9. **Versioning our APIs**
204+
### 9. Versioning our APIs
205205
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](https://semver.org/) (for example, 2.0.6 to indicate major version 2 and the sixth patch) like most apps do nowadays.
206206

207207
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. This is even relevant for mobile apps. For example, if an Android app integrates with our API, it can sometimes be hard to ensure all users update their app to the latest version. So for gradual phasing out, a previous version of the API can still stay active for the subset of users on the old app.

module1-introduction-to-backend/r4-server-side-validation/README.md

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Server-side Validation
22
Your API can handle different types of requests now. But does it validate the request data before processing it? The objectives of this lesson are:
33
1. Understanding the need for server-side validation
4-
2. Getting familiar with implementing server-side validation
4+
2. Getting familiar with implementing validation
5+
3. Understanding the characteristics of a good error response
56

67
## What is Validation?
78
Validation can mean a lot of things, but in API land it generally means figuring out if the data being sent to the API is any good or not. Validation can happen both on client-side before sending the request or on server-side when receiving the request. Client-side validation is generally used to provide quick feedback to a user. For example, when you're submitting a form and you see a field highlighted in red saying "Required field". Basically the client validated that the field cannot be empty. However, an API must not entirely rely on client-side validation. We have talked about how the frontend need not know the underlying implementation of an API. Similarly, an API does not know what actually happened on the frontend when it receives a request from a client. That is why more often than not, the first step in processing a request is to validate the data that came with it.
@@ -61,7 +62,75 @@ Now, whenever a request that includes invalid username or password fields is sub
6162

6263
Basic validations can be handled by such middleware. However, custom validations based on specific business rules may require custom code. In any case, your server-side application must have a high level of validation across API routes and test cases ensuring that each and every erroneous possibility is checked and handled by sending a validation error response. A good validation response will help the frontend developers using your API to display graceful error messages to the end-user.
6364

65+
## What makes a good error response?
66+
Error messages are almost the last thing that any developer wants to see in an API response. But an error message that doesn't tell the developer anything about what went wrong is even worse. Error codes and error messages are probably the most useful diagnostic element in the API space, and it is surprising, how little attention sometimes developers pay them.
67+
68+
Error codes in the response of an API is the fundamental way in which a developer can communicate failure to a user as well as jump-start the error resolution process. A user doesn’t choose when an error is generated, or what error it gets. So error responses are the only truly constant, consistent communication the user can depend on when an error has occurred. Error codes have an implied value in the way that they both *clarify the situation*, and communicate the *required solution*.
69+
70+
Consider for instance an error code such as `401 Unauthorized – Please Pass Token`. In such a response, you understand the point of failure, specifically that the user is unauthorized. Additionally, however, you discover the intended functionality — the API requires a token, and that token must be passed as part of the request in order to gain authorization.
71+
72+
Essentially there are three parts to a good error response:
73+
1. **An HTTP Status Code**: We have already talked about these in the previous lessons on API best practices and REST APIs.
74+
2. **An Internal Reference ID**: For documentation-specific notation of errors. In some cases, this can replace the HTTP Status Code, as long as the internal reference sheet includes the HTTP Status Code scheme or similar reference material.
75+
3. **Human readable messages**: To summarize the context, cause, and general solution for the error at hand.
76+
77+
Let's look at some examples.
78+
79+
### Twitter API
80+
Let’s attempt to send a GET request to retrieve our mentions timeline.
81+
```
82+
https://api.twitter.com/1.1/statuses/mentions_timeline.json
83+
```
84+
85+
When this is sent to the Twitter API, we receive the following response:
86+
```
87+
HTTP/1.1 400 Bad Request
88+
x-connection-hash:
89+
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
90+
set-cookie:
91+
guest_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
92+
Date:
93+
Thu, 01 Jun 2017 03:04:23 GMT
94+
Content-Length:
95+
62
96+
x-response-time:
97+
5
98+
strict-transport-security:
99+
max-age=631138519
100+
Connection:
101+
keep-alive
102+
Content-Type:
103+
application/json; charset=utf-8
104+
Server:
105+
tsa_b
106+
107+
{"errors":[{"code":215,"message":"Bad Authentication data."}]}
108+
```
109+
110+
Looking at this data, we can generally figure out what our issue is. First, we’re told that we’ve submitted a 400 Bad Request. This tells us that the problem is somewhere in our request. Our content length is acceptable, and our response time is well within normal limits. We can see, however, that we’re receiving a unique error code that Twitter itself has denoted — “215”, with an attached message that states “Bad Authentication data”. This tells us that the fix is to supply authentication data, but also gives us a number to reference on the internal documentation of the Twitter API for further details.
111+
112+
### Facebook
113+
Let’s pass a GET request to ascertain some details about a user. All personal information will be blanked out for security purposes.
114+
```
115+
https://graph.facebook.com/v2.9/me?fields=id%2Cname%2Cpicture%2C%20picture&access_token=xxxxxxxxxxx
116+
```
117+
118+
This request should give us a few basic fields from this user’s Facebook profile, including id, name, and picture. Instead, we get this error response:
119+
```
120+
{
121+
"error": {
122+
"message": "Syntax error \"Field picture specified more than once. This is only possible before version 2.1\" at character 23: id,name,picture,picture",
123+
"type": "OAuthException",
124+
"code": 2500,
125+
"fbtrace_id": "xxxxxxxxxxx"
126+
}
127+
}
128+
```
129+
130+
While Facebook doesn’t directly pass the HTTP error code in the body, it does pass a lot of useful information. The “message” area notes that we’ve run into a syntax error, specifically that we’ve defined the “picture” field more than once. Additionally, this field lets us know that this behavior was possible in previous versions, which is a very useful tool to communicate to users a change in behavior from previous versions to the current. Additionally, we are provided both a code and an `fbtrace_id` that can be used with support to identify specific issues in more complex cases. We’ve also received a specific error type, in this case `OAuthException`, which can be used to narrow down the specifics of the case even further.
131+
64132
---
65133
## References
66134
- https://www.apisyouwonthate.com/blog/server-side-validation-with-api-descriptions
67-
- https://express-validator.github.io/docs/
135+
- https://express-validator.github.io/docs/
136+
- https://nordicapis.com/best-practices-api-error-handling/
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Server-side Validation Setup
2+
3+
Assignment repo link to be inserted
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Server-side Validation Additional Practice
2+
3+
Assignment repo link to be inserted

0 commit comments

Comments
 (0)