REST is an architectural style that defines a set of constraints and properties to allow communication between various systems on the web.
-
client-server architecture:
separation of concerns, UI and API are have their own responsibilities, and therefore allow both to evolve independentlycacheability:
responses are implicitly or explicitly defined as cacheable or not to prevent clients from reusing stale or inappropriate data in resonse to furth requestsstatelessness:
no client context is stored on the server between requests, each request from the client must contain all the information necessary to service the requestLayered System:
like the MVC concept, server components are built on top of each other to build a heirarchy that helps create a more scalable and modular appliation
-
- SOAP
- relies exclusively on XML to provide message services
- is more rigid than REST
- is highly extensible, pick and choose what you need from it
- provides build in error handling with standardized codes for some automated error handling
- differences
- SOAP is standardized and language, platform, and transport independent
- SOAP works will in distributed enterprise environments
- REST is easier to learn, efficient, and fast
- SOAP
Whats new? New render return types - better error handling - portals - better server-side rendering - reduced file size
Fragments and strings: return an array of elements
, string
, or number
from a component's render method
class MyArrayComponent extends React.Component {
render() {
return [
<div key="1">first element</div>,
<div key="2">second element</div>
];
}
}
class MyStringComponent extends React.Component {
render() {
return "hey there";
}
}
Error boundaries: special components that capture errors inside their subtree and display a fallback UI in its place - like try catch - instead of unmounting whole componenet from root
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
Then you can use it as a regular component:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
Note: error boundaries only catch errors in the components below them in the tree. It cannot catch an error within itself
Uncaught errors: errors that were not caught by any error boundary will result in unmounting the whole react component tree
We’ve built a generic way for components to suspend rendering while they load async data, which we call suspense. You can pause any state update until the data is ready, and you can add async loading to any component deep in the tree without plumbing all the props and state through your app and hoisting the logic.
New packaging stragety: by eliminating process.env
from Node.js, React 16 is able to improve SSR by 300%
- backwards compatible: syntax from react 15 works with react 16
- client-side rendering
render()
when solely rendering on the clienthydrate()
when rendering on top of server-side rendered markup
- not supported: error boundaries or portals in SSR
Flat bundle format: consistent size no matter what react is shipped with
Node environment (ssr): only checks process.env.NODE_ENV
once at the beginning so no need to compile. React 16 doesn't need to compile because there is no need to remove references to process.env
like in react 15
Backpressure (ssr): if the network is backed up and can't accept more bytes, the renderer gets a signal and pauses rendering until clog is cleared up. The server uses less memory and is more responsive to I/O connections
How to prevent it from happening
Prevent hackers from forcing your sql queries to do things it was not intended to do. For SQLite, use sqlite3_prepare()
to create a statement object. Try: strongly validate all data or excape all user-supplied input using an escaping routine specific to your database vendor.
Should be used as a last resort, when nothing else is feasible - this method is frail when compared to other methods and cannot guarantee it will prevent all sql injection. This is only recommended to retrofit legacy code or low risk tolerance applications.
Use something like ModSecurity's ever-evovlving set of rules to filter potentially dangerous we requests (avail free for Apache, Microsoft IIS, and Nginx).
Create multiple databases with minimum levels of privilege for their usage environment. This way, a breach through one channel cannot be leveraged to compromise the entire database.
Keep them local and if needed in external use, keep them generic
Caution These methods of escaping only works when the NO_BACKSLASH_ESCAPES
SQL mode is disabled (it is enabled by defaul in MySQL servers).
In order to avoid SQL Injection attacks, you should always escape any user provided data before using it inside a SQL query. You can do so using the mysql.escape()
, connection.escape()
, or pool.escape()
methods:
var userId = 'some user provided value';
var sql = 'SELECT * FROM users WHERE id = ' + connection.escape(userId);
connection.query(sql, function (error, results, fields) {
if (error) throw error;
// ...
});
Alternatively, you can use ?
characters as placeholders for values you would want to escape. Multiple placeholders are mapped to values in the same order as passed. For example, in the following query foo
equals a
, bar
equals b
, baz
equals c
, and id
will be userId
:
connection.query('UPDATE users SET foo = ?, bar = ?, baz = ? WHERE id = ?', ['a', 'b', 'c', userId], function (error, results, fields) {
if (error) throw error;
// ...
});
Escaping query identifiers: if you can't trust an sql identifier (database/table/column_name) use mysql.escapeId(identifier)
, connection.escapeId(identifier)
, or pool.escapeId(identifier)
.
var sorter = 'date';
var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId(sorter);
connection.query(sql, function (error, results, fields) {
if (error) throw error;
// ...
});
Preparing queries: you can use mysql.format()
to prepare query with multiple insertion points. This method safely prepares a query for the database.
var sql = "SELECT * FROM ?? WHERE ?? = ?";
var inserts = ['users', 'id', userId];
sql = mysql.format(sql, inserts);
Programming paradigm based upon objects that aims to be modular and reusable
OOP is a programming paradigm organized around objects rather than "actions" and data rather than logic.
Important features
- bottom-up approach in programming
- programs organized around objects, grouped in classes
- focus on data with methods to operate upon object's data
- interaction between objects through functions
- reusability of design through creation of new classes by adding features to existing ones
SOLID is a basic object oriented design (OOD) principle. Its purpose is to ensure that you construct your classes in maintainable and reusable manner, and to limit any modification pain.
Letter | Concept | Explanation |
---|---|---|
S | Single Responsibility Principle | Classes should have only one responsibility |
O | Open/Closed Principle | Objects should be open for extension, but closed for modification |
L | Liskov Substitution Principle | Subtypes of an object should be able to substitute other objects of that subtype |
I | Interface Segregation Principle | Clients should not be forced to depend upon interfaces they don't use |
D | Dependency Inversion Principle | Entities must depend on abstractions not on concretions |
I've attached great articles to read on each principle but let me try to condense it into easily digestable segments
a class should have only ONE responsibiliy.
Why? - If a class has too many responsibilities, any change to one class may cascade down to all the other classes.
Good Example:
class User {
fetchUser() {
return db.query(SQL-STATEMENT)
}
createUser() {
return db.query(SQL-STATEMENT)
}
}
Bad Example:
class User {
fetchUser() {
return db.query(SQL-STATEMENT)
}
fetchPosts() {
return db.query(SQL-STATEMENT)
}
createPosts() {
return db.query(SQL-STATEMENT)
}
}
Very simple, in the bad example, the User class is, for some reason, responsible for fetching and creating posts. The problem inherent in this is that if you wanted to change a method in the parent class, then you'll have to change all the subclasses created from that parent object. unrelated some shit
write code that doesn't have to be changed every time the requirements change.
Example:
Let's say we wanted to create a class that can calculate the area of a rectangle.
We start by creating a class for a rectangle:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
Then we create a class for an area calculator:
class AreaCalculator {
calculateArea(height, width) {
return height * width;
}
}
Now we can instantiate an instance for both classes and pass in the rectangle specs to the area calculator:
const rect = new Rectangle(5,3);
const area = new AreaCalculator();
area.calculateArea(rect.height, rect.width); // => 15
The problem becomes evident here. If you wanted to be able to calculate the area of another shape like a triangle, then you'd have to rewrite the AreaCalculator class to handle another shape.
This violates the 'closed for modification' clause as now you are forced to rewrite some code.
Solution?
const areaCalculator = (shapes) => {
let area = 0;
shapes.forEach((shape) => {
area += shape.area();
})
return area;
}
class Shape {
area() {
return area;
}
}
class Rectangle extends Shape {
constructor(height, width) {
this.height = height;
this.width = width;
}
area() {
return this.height * this.width;
}
}
class Circle extends Shape {
constructor(radius) {
this.radius = radius
}
area() {
return this.radius * this.radius * Math.PI
}
}
const rect = new Rectangle(5,3);
const circ = new Circle(2);
areaCalculator([rect, circ]);
Now we have this nice global area calculator that will give you the total area of any number of shapes.
This is where it starts to get a little convoluted...
If S is a subtype of T, then objects of type T may be replaced by objects of type S.
What...?
That basically means subtypes must be able to substitute for their base types.
Still what? Here's an example.
Example:
class Car {
drive() {
return 'we driving';
}
fillUpGas() {
return 'filling up!';
}
}
class Tesla extends Vehicle {
}
class Driver {
constructor(vehicle) {
this.vehicle = vehicle;
}
start() {
this.vehicle.fillUpGas();
this.vehicle.drive();
}
}
const myCar = new Tesla();
const me = new Driver(myCar); // => this will throw an error
Driver using a Tesla will run into an error as a Tesla does not have the fillUpGas method. Thus, in programming and according to our definition, a Tesla really isn't a car.
The LSP forces us to think about reality and definitions in a different way. Although a Tesla is in fact a car in real life, our current class does not support it. Thus, in our own definition, a Tesla is not a car but a mere representation of a car.
clients should not be forced to depend upon interfaces they don't use
Example:
class Shape {
area() {
return area;
}
volume() {
return volume;
}
}
Not all shapes will have a volume associated with it. Thus, it's better to simply create two separate classes; one for each operation.
class Area {
calculate() {
return area;
}
}
class Volume {
calculate() {
return volume;
}
}
Now, we don't have any unnessary methods associated to a class!
depend on abstractions not on concretions
Bad Example:
class PasswordReminder {
connect(mySQL) {
mySQL.connect();
}
}
Good Example:
class DBConnector {
constructor(db) {
this.db = db;
}
connect() {
this.db.connect();
}
}
class PostgreSQL extends DBConnector {
connect() {
return 'Database connected';
}
}
class PasswordReminder {
connect(DBConnector) {
return 'connected';
}
}
So, as you can see, if we wanted to change DBs then we'd have to change password reminder to reflect that. This violates the Open/Closed principle.
So instead, we create a different class that simply connects the DB for us. That way, any changes in the DB is simply extending the class of DBConnector.