Middlewares, Global Catches, Data Validation and Auth Basics
1/21/26
middlewares
- what is the best place to do these prechecks ?
- eg: authentication and input validation
- do we need to write custom logic each time to check ? hint: for some things yes!
- this is the job of middlewares - to do things that is either business logic or something that is not part of the main routing and request logic that can’t be handled by express.
- the dumb way of input validation is to take everything in query parameters or headers, and do it in the routing logic itself without seperating into business logic.
//a scratch middleware
app.get("/health-checkup", (req, res) => {
//auth for now
const userName = req.headers.userName;
const passwd = req.headers.passwd;
const kidneyId = req.query.kidneyId;
if (!userName === "sagnik" && passwd === "pass") {
res.status(403).json({
msg: " something wrong with inputs",
});
return;
}
if (kidneyId != 1 || kidneyId != 2) {
res.status(403).json({
msg: "something wrong with inputs",
});
return;
}
res.status(200).json({
msg: "input validataion done!",
});
//a scratch middleware
app.get("/health-checkup", (req, res) => {
//auth for now
const userName = req.headers.userName;
const passwd = req.headers.passwd;
const kidneyId = req.query.kidneyId;
if (!userName === "sagnik" && passwd === "pass") {
res.status(403).json({
msg: " something wrong with inputs",
});
return;
}
if (kidneyId != 1 || kidneyId != 2) {
res.status(403).json({
msg: "something wrong with inputs",
});
return;
}
res.status(200).json({
msg: "input validataion done!",
});
});
}); - what if we tell you to introduce another route that does kidney replacement ? inputs need to be the same
- it would be a post route
- we would again need to do input validation there again
- violates the DRY principle
- best if we abstract that logic elsewhere and check it there itself.
- the real optimum to do all these things is to do prechecks using middlewares
- middlewares is just another function that is specialized for that.
- we can also then pick and choose various different middlewares for different things
- How does middlewares pass that information to the other middleware and how does it interact with other middleware ?
- In the express http functions, instead of one callback functions, we can in fact give a range of callback functions.
- there are 3 inputs to the callbacks, res,req,next
- next we can call , if we think the things are fine
- req, res used for pre-checks
- if things are fine , then we chain it back to next
- under the hood, express handles this
- there can be multiple functions , instead of just one handler.
app.use()- only then can we catch any body that the response sends in the POST body handler.
- this middleware is going to be called everywhere.
- instead of passing it explictly, anything that comes after the
app.use()line will have th middleware inside it.
input-validation
- need some checks on the data that we are sending.
- a very basic way is to catch the fields that we are passing in the request parameters and we check on that.
- cause backends are open and people can send request with any type of inputs / wrong inputs.
- as a application developer, its our duty to do sanity checks and do input validation so that server doesnt crash.
- another type of middleware in js
- Problem: someone else can read your exception eg: end-client can read the exception.
- end user should see a clean repr of what happened.
- express handles this under the hood
- but better way to do is to use a global catches.
- so we put
app.use(express.json())before all the code - so any exception that is being caused, is handled in the global catches.
- error handling middlewares:
- Special type of middleware function in Express that has 4 arguments instead of three
(rerr,req,res,next) - Express will recognize this as an error handling middleware because of these 4 arguments.
- Special type of middleware function in Express that has 4 arguments instead of three
- zod library:
- to do input validation.
- better way to do validation
- prebuilt template and logic to check how to do input validation.
- In Zod , what we are doing basically is parsing the input and checking on that
- We define the schema on what the data should be like and zod will parse based on that.
- zod gives us everything and gives us a bunch of error messages
- super useful if you wanna show that in the UI.
- some methods are present that we need to use it and use
safeParseand the success object to check if validation passed or not.
authentication and JWT
- anyone can send requests to your backend
- how to ensure that this user has access to a certain resource ?
- dumb way - ask user to send username and password
- Better way-
- give the user back a token on signup / login
- ask the user to send back the token in all future requests
- when the user logs out, ask the user to forget the token (or revoke it from the backend)
- ![[Screenshot 2026-01-21 at 6.23.32 PM.png]]
- Either an authorization header and this token is how we do signin / signup in websites.
- User will creating this request and this is how we do authorization for it.
- Project :
- let people sign up to your website
- only allowed signed in users to see people( create a dummy people list)
- only allowed the signin users can access the content.
- before we learn authentication, some cryptography jargon:
- hashing
- before we save the passwords to the db , we hash it, so that only users can access that.
- and the hashed password will be stored in database
- hashing -> converting a simple string to complicated gibberish, so that its only one way ; can never guess -> can never decrypt back to password.
- how does the backend server validate this ?
- backend server reconverts back to the string and compares it.
- first inmemory we convert it back to the string and check if it is the same thing
- one way (convert a string to hash )
- Encryption
- this is 2 way
- can always convert the hashed result to the original string , provided we have some key.
- hashing doesnt really require a password, encryption requires a password.
- a string is encrypted using a password
- string can also be decrypted using the same password.
- JSON web tokens
- some hashing function, but only works for JSON input
- gives more structured data -> more longer string
- in the end if we look at it, it is significantly different from hashing and encryption.
- takes JSON as a input
- Token:
- it takes this and creates a token in the end
- JWT
- by default whoever has this input , can convert it back to the original object that we see.
- anyone who has the string , can look at the original string that was formatted.
- conversion anyone can do , but verification of this string can only be done by authorized backend servers that has created it.
- given the string and the password, can pass it through a verification function and only return the original data , if the original thing was used to verify that.
jwt.verify(<passwd>)and can verified, only then we can authenticate the user.- very similar to encryption and decryption.
- ![[Screenshot 2026-01-21 at 8.13.01 PM.png]]
jwt.verify()will only allow if the user if true, otherwise not.
- its neither of encryption or hashing (its technically a digital signature)
- anyone can see the original output given the signature
- signature can be verified only using the password.
- Local Storage
- a place in your browser where we can store some data
- usually things that are stored include:
- authentication tokens
- user language preferences
- user theme preferences
- etc
- hashing
fetch
- to actually send requests from frontend to backend
- hitting the endpoint either by a browser or insomnia
- how to send a request when the button is sent.
- part of browser API , to hit to the backend server
- browsers expose something call fetch that hits the backend servers.
- ![[Screenshot 2026-01-21 at 6.47.06 PM.png]]
- using the
fetchapi globally to fetch the responses - on right clicking and clicking on the console, we can see the response.
- Eg: using the
fakerapi , we can try for free some data and see their response. - whenever the data resolves, the promise should reach there.
- fetch will return a Promise and using the promisified response to resolve based on that.
Databases
- until now, we have been storing data in memory , bad for a few reasons
- data can’t be dynamic , if we update in memory objects, the updates are lost if the process restarts
- there are multiple servers in the real world
- databases also support for querying and for optimized storage of the data.
- In the real world, you dont have a single backend server, you have a cluster
- ![[Screenshot 2026-01-21 at 9.00.31 PM.png]]
- for most purposes we have multiple servers and connected to a single db (well not really in prod!), but still
- BE server fleet do business logic, do auth and other things.
- user hits the backend
- backend hits the database
- user doesnt have access to the database/ can’t talk to the DB.
- how is the db exposed, how does be knows where to connect and etc?
- only the backend server knows the URL of the databases
- need to store secrets and also other things
- there are various types of databases, depending on querying and the type of data that we want to store
- graph dbs
- vector dbs
- sql dbs
- nosql dbs
- very famous nosql db -> mongodb
MongoDB
- MongoDB lets us create databases
- In each DB, it lets us create tables(collections)
- in each table, it lets us dump JSON data.
- It is schemaless
- It scales well and is a decent choice for most use cases.
- How to work with it ?
- create a mongodb instance
- get our mongodb connection URL
- download mongodb compass and explore the DB
- while creating databases we have to create database-table and a collection name for the same.
- can create multiple database and create multiple collections(tables)
- create a new signup endpoint that users can come and hit it to enter the details to our database.
- can then do CRUD operations in the database.
- How does backend connects to the database ?
- using libraries !
- express lets u create an HTTP server
- jsonwebtokens library lets us create jwts
- mongoose lets us connect to your database
- mongodb is schemaless, but for normal CRUD operations we need some abstraction to add to item to a mongodb database
- use the mongoose library to give schema and structure to it.