Opinionated Framework Part 1 - Setup and Routing
What you will learn
- What is an Opinionated Framework?
- How to start up a new project
- How to create a controller and actions
- How to connect controller actions to routes
Prerequisites
- Dart SDK 2.12 or higher (if you install flutter it comes with the Dart SDK)
- Heroku CLI
- Postgres 12 or higher
What is an opinionated framework?
Most web frameworks for building full-stack websites or APIs/Microservices fall into one of two categories.
- Minimalist: Like express, these frameworks provide you the basic features to create a web server leaving it up to you to decide which libraries to use for connecting to databases, encryption, authentication, etc. and how to structure your files and folders in your workflow.
- Opinionated: These frameworks usually come with CLI tools that allow you to spin-up new projects with pre-defined projects structures, conventions and all the libraries for full functionality already built in.
These are the main web frameworks across many languages:
Language | Minimalist | Opinionated |
---|---|---|
Javascript | Express, Koa | Nest, Foal, Sails |
Python | Flash, FastAPI | Django, Masonite |
Ruby | Sinatra | Ruby on Rails |
PHP | Slim | Laravel |
GO | Echo | Buffalo, Revel |
Rust | Warp | Rocket |
Java | Spring Boot | |
C# | .Net | |
Dart | Shelf | Aqueduct, Angel |
Kotlin | kTor, Javalin | Spring Boot |
Swift | Vapor, Kitura |
Setting Up the Project
At the time of writing this, the Aqueduct and Angel frameworks needed to update their frameworks for recent changes in the Dart language. So we will be using Shelf, the minimalist framework for Dart which is most similar to KoaJS. We are using the shelf_router library as well which makes the feel more express like. We will be using a template that spins up a lot of the basic structure for us.
- create a new project with the command
npx degit AlexMercedCoder/DartShelfPostgresTemplate#main firstproject
- cd into the folder and run
pub get
to download all neccessary libraries (pub is like npm for Dart) dart run web/server.dart
to run and test the webserver and check to see if you see hellow world at localhost:7777
Creating a Controller
In this template the server is initiated in web/server.dart. In this file the HomeController is mounted as the servers handler. Our HomeController will be the heart of our routing logic. What we will do is create a new controller and mount it.
Step 1 - Make a New Controller
In the controllers folder create a file called FirstController.dart and add the following below.
// import 'dart:async' show Future;
import 'package:shelf_router/shelf_router.dart';
import 'package:shelf/shelf.dart';
import '../cors.dart';
import '../models/initiateModels.dart';
import 'dart:convert';
// import 'package:shelf/shelf_io.dart' as shelf_io;
class FirstController {
// By exposing a [Router] for an object, it can be mounted in other routers.
// We are defining a custom getter that returns the router
Router get router {
final router = Router(); // CREATE A NEW ROUTER
// get request to "/first"
router.get('/', (Request req){
return Response.ok("The First Controller", headers: cors);
});
// catch all for "/first"
router.all('/<ignored|.*>', (Request request) => Response.notFound('No FirstController Route Available'));
return router;
}
}
Now let's mount our controller to HomeController below the test controller
// Make sure to import the controller
import './FirstController.dart';
/////////////////////////////////////////////
////////////////////////////////////////////
// Mount Other Controllers Here
router.mount('/test/', TestController().router);
// Mount FirstController Here (trailing slash required)
router.mount('/first/', FirstController().router);
- Lets turn on the server
dart run web/server.dart
and checkoutlocalhost:7777/first/
Writing Routes
Now that we've created our controller, let's add some more routes to our controller.
// get request to "/first/first" sends back a json response
router.get('/first', (Request req){
// create a new Map
var response = {"Hello": "World", "More": "data"};
// Encode the MAP as JSON and send response
return Response.ok(jsonEncode(response), headers: cors);
});
Let's create another route that uses a URL Parameter
// get request to "/first/<second>" sends back a json response with the param
router.get('/<second>', (Request req, String second){
// create a new Map
var response = {"Param": second};
// Encode the MAP as JSON and send response
return Response.ok(jsonEncode(response), headers: cors);
});
- restart the server and go to /first/IMadeAParam in your browser
Accessing URL Queries
Let's modify that last route to add a query into the Map.
// get request to "/first/<second>" sends back a json response with the param
router.get('/<second>', (Request req, String second){
// pull the "query" query from the url
var query = req.url.queryParameters["query"];
// create a new Map
var response = {"Param": second, "query": query};
// Encode the MAP as JSON and send response
return Response.ok(jsonEncode(response), headers: cors);
});
restart server and go to http://localhost:7777/first/thisisaparam?query=thisisaquery
Accessing the Request Body
Let's create one more post route to receive the request body!
// post request to "/first/third/body" sends back a json response with the body
router.post('/third/body', (Request req) async {
// get the body of the request as a string
var stringBody = await req.read().transform(Utf8Decoder()).join();
// turn the string into a map
var stringBody2 = await jsonDecode(stringBody.toString());
// convert the map into json
var body = jsonEncode(stringBody2);
// Encode the MAP as JSON and send response
return Response.ok(body, headers: {...cors, "Content-Type": "application/json"});
});
Now let's head to lab!
Resources to Learn More
Google Shelf Documentation Shelf Router Documentation