This content originally appeared on DEV Community and was authored by Aswin Gopinathan
Hello everyone!
In my previous article i talked about building APIs with Dart using the shelf package, and how you can host it on Heroku.
If you haven't read that, do read it if you are new to building APIs in Dart.
You can access the article from here
In this article, i am gonna talk about how you can implement various http methods (GET, POST, DELETE) using an add-on to the shelf package, which is the shelf-router package.
A small limitation to the shelf
package is that it dosen't have support for creating separate handlers for different request methods/ request URLs built into it.
For eg:
The various request methods can be a GET/POST/DELETE/PUT request using different URLs, or these requests can be on the same URL as well. But we have to explicitly write switch or myriad of if conditions to redirect to its appropriate handlers for further request handling.
But the shelf_router
package defines a Router
object which we can use to define the url and http method that is requesting the service and implicitly transfer control to appropriate handler functions.
Now, it sounds interesting right! So, without any further delay lets dive right in.
Note: I will be using IntelliJ as the IDE throughout this article, but you can replicate the same in VsCode or Android Studio.
You can skip the following section if you know how to create a new dart-server
project.
Create a new dart-server project
Click on File->New Project
Name it anything you want!
After these two steps, you should get the template for your dart-server
ready!
Now, head over to lib->server.dart. Your file should look like this:
import 'dart:io';
import 'package:args/args.dart';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
// For Google Cloud Run, set _hostname to '0.0.0.0'.
const _hostname = 'localhost';
void main(List<String> args) async {
var parser = ArgParser()..addOption('port', abbr: 'p');
var result = parser.parse(args);
// For Google Cloud Run, we respect the PORT environment variable
var portStr = result['port'] ?? Platform.environment['PORT'] ?? '8080';
var port = int.tryParse(portStr);
if (port == null) {
stdout.writeln('Could not parse port value "$portStr" into a number.');
// 64: command line usage error
exitCode = 64;
return;
}
var handler = const shelf.Pipeline()
.addMiddleware(shelf.logRequests())
.addHandler(_echoRequest);
var server = await io.serve(handler, _hostname, port);
print('Serving at http://${server.address.host}:${server.port}');
}
shelf.Response _echoRequest(shelf.Request request) =>
shelf.Response.ok('Request for "${request.url}"');
This is the approach using the shelf
package.
Now, lets add the shelf-router package to our pubspec.yaml
file.
shelf_router: ^1.1.2
This is the version of the package at the time of writing this article. Please update it accordingly.
Next, lets create a new dart file and name it api.dart
. We will write all the routing logic in this file.
Add imports to the shelf
and shelf_router
package as follows:
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
Next, create a new class Api
and write a getter method handler
that returns a Handler
object.
class Api {
Handler get handler {
final router = Router();
// Write your logic here
return router;
}
}
Now, go back to the server.dart
file and update the last-second line of the main function as follows:
var server = await io.serve(Api().handler, _hostname, port);
You may have to import the api.dart file as well.
Now, your server will serve Requests from this new API Class handler rather than from the typical _echoRequest()
function.
Now, lets get back to the api.dart
file. All the codes thats gonna follow will be written inside the handler
method below the comment.
GET Request
First, lets see how a get request for the url /api/<name>
will work, where <name>
can be any word:
router.get('/api/<name>', (Request request,String name) {
return Response.ok(
jsonEncode({
'success':true,
'data':name
}),
headers: {
'Content-type':'application/json'
},
);
});
Here, we have defined a handler method to process in case a GET request is made to the url /api/<name>
, where name could be any word.
The Handler method will return an object of type Response
with OK status code. The response will be a JSON String as follows:
Since we have added a placeholder in the url parameter of router.get()
, ie <name>
, the handler method will take a second parameter that will contain the value passed in the url, which is in-turn returned as Response.
For the remaining part of this article i am going to use a local json file which has pre-filled data.
You can access the file from here.
The file contents should look like this:
{
"users":[
{
"id":1,
"Name":"Rachel Green",
"Age": 32
},
{
"id":2,
"Name":"Ross Geller",
"Age": 33
},
{
"id":3,
"Name":"Monica Geller",
"Age": 32
},
{
"id":4,
"Name":"Joey Tribbiani",
"Age": 31
},
{
"id":5,
"Name":"Chandler Bing",
"Age": 33
},
{
"id":6,
"Name":"Pheobe Buffay",
"Age": 34
}
]
}
Now, inside the Api class we will define a variable data
of type List
, which will store the list of users from the json file.
List data = jsonDecode(
File('friends.json').readAsStringSync()
)['users'];
POST Request
Now, lets see how we can perform a POST Request. Let's perform a add operation, where we will add new information from the POST payload.
POST Payload is the information you pass when hitting the API Endpoint using a POST Request.
router.post('/api/add', (Request request) async{
final payload = jsonDecode(await request.readAsString());
data.add(payload);
return Response.ok(
jsonEncode({
'success':true,
'data':data
}),
headers: {
'Content-type':'application/json'
},
);
}
);
I will be using Postman to perform the POST Operation on the API. You can try any API Testing tools of your choice.
I will be passing the following data as the payload(body) for the POST Request:
{
"Id": 7,
"Name":"Aswin",
"Age":22,
}
This json data will be stored in the variable payload
in the handler function above, and the same is added to the data
variable which is then displayed as a response to verify it is added correctly.
This is a basic application of how you can use the POST request. Some improvisations can be:
As of now, we are just appending the new data to the variable
data
and not to the actual json file. That means, when you restart the server, the appended data will be gone as that time you are freshly fetching from the json file.
You can change the code to append the new data permanently to the json file.We are passing the hardcoded Id in the payload, you can improvise it by just passing the Name and Age and calculating its Id in the function by considering the Id of the last entry in the json data.
DELETE Request
Finally, lets see how the DELETE Request works.
router.delete('/api/delete/<id>', (Request request, String id) {
final idN = int.parse(id);
final deletedData = data.firstWhere(
(element) => element['id']==idN,
orElse: ()=> null
);
if(deletedData==null) {
return Response.notFound(
jsonEncode({
'success':false,
'data':'invalid id'
}),
headers: {
'Content-type': 'application/json'
},
);
}
int pos = deletedData['id'];
data.removeAt(pos-1);
return Response.ok(
jsonEncode({
'success':true,
'data':deletedData
}),
headers: {
'Content-type':'application/json'
}
);
});
URL : api/delete/<id>
We first get the id passed in the URL and store it in the variable idN
. Next, we use that variable to search if any entry exists in the data
variable with the given id.
If it dosen't exist null
is returned and we return a Response object of status code 404(Not Found).
If Id exists, we remove the entry from the data
variable and the deleted information is returned in the Response with a status code of 200(OK).
Well folks, thats it for now. You can access the above codes on GitHub.
Where can you go from here
In this article you learned how you can read/add/delete from a database(here, a json file).
You can take it to the next level by connecting to a online database like Supabase, Firebase, Mongo, etc and make a production level API.
You can also add a Authentication or Authorization check and allow only the requests that sends a Valid Bearer Token to perform the operations.
If you have any doubts while working with the codes in this article, feel free to reach out to me on my handles:
Twitter : @GopinathanAswin
LinkedIn : Aswin Gopinathan
So, it's a good bye then. I will see you in my next article!
This content originally appeared on DEV Community and was authored by Aswin Gopinathan
Aswin Gopinathan | Sciencx (2021-11-15T17:59:21+00:00) Build APIs for various HTTP Methods in Dart. Retrieved from https://www.scien.cx/2021/11/15/build-apis-for-various-http-methods-in-dart/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.