Dart Server with good practices

Shashiben
4 min readJul 24, 2022

Once upon a time in the land of programming wonders, there existed a mighty language called Dart. And in this kingdom, developers sought to create powerful servers, much like those forged in the fiery depths of Node.js. But lo and behold, they wished not just for servers, but for servers built with the finest practices, structured like the most elegant of castles, and armed with the sharpest of code.

Thus, our tale begins with a journey to create a Dart server with practices so good, they’d make even the most seasoned knights nod in approval. But before we embark on our adventure, we must first gather our tools. Fear not, for the path is clear:

🔧 First, summon the Dart spirits with the incantation

dart create server_name

to create our fortress.

Creating a server

With our stronghold laid out, it’s time to fortify it with dependencies:

  • 📦 shelf, for handling requests like a seasoned gatekeeper.
  • 🚪 shelf_router, to guide travelers on the right paths.
  • 📁 shelf_static, to serve static files with the swiftness of a messenger.
  • 🔥 shelf_hotreload, for magical powers to reload code on the fly.
  • 🛠️ args, for wielding command-line arguments with finesse.

Now that our armory is stocked, let us shape the very foundation of our domain: the folder structure.

  • 📦 bin
    ┣ 📂constants
    ┃ ┗ 📜api.constants.dart
    ┃ ┗ 📜string.constants.dart
    ┣ 📂controllers
    ┃ ┗ 📜user.controller.dart
    ┃ ┗ 📜auth.controller.dart
    ┣ 📂db
    ┃ ┗ 📜setup.db.dart
    ┣ 📂routes
    ┃ ┗ 📜user.routes.dart
    ┃ ┗ 📜auth.routes.dart
    ┣ 📂utils
    ┃ ┗ 📜extension.utils.dart
    ┃ ┗ 📜string.utils.dart
    ┃ ┗ 📜date.utils.dart
    ┣ 📜server.dart

Server file

🏰 Behold, the majestic structure of our ‘bin’ directory, where constants, controllers, routes, utilities, and the heart of our server reside.

In the above function , in the updateHandler variable you can ignore the middle ware corsHeaders(), if you want to add cors then you need to add that line.

Constants

  • 💬 String constants, the unwavering beacons of text in a sea of code.
class StringConstants{
static const String mongoUrl="";
}

🔑 And beyond, the sacred grounds of API constants, safeguarding keys to realms unknown like twilio,mailchimp etc..

DB Setup

But what is a fortress without its guardians? We turn to the guardians of data, the noble DB Setup:

🗃️ With mongo_dart as our ally, we lay the groundwork for MongoDB, crafting a sanctuary for our data to dwell.

class DBSetup {
DbCollection userRef = db.collection('users');
DbCollection mortgageRef = db.collection('mortgage');
static late Db db;
static init() async {
try {
db = await Db.create(StringConstants.mongoUrl);
await db.open();
print("Mongo is connected");
} on MongoDartError catch (e) {
print('Mongo Exception: ${e.message}');
} catch (e) {
print("Exception: $e");
}
}
}

Utils

Amidst the bustling lanes of utilities, we forge tools of unparalleled utility:

  • 🛠️ Extensions, to extend the reach of our code.
  • 🔤 Strings, wrangled and tamed for our purposes.
  • 📅 Dates, the sands of time at our fingertips.

Routes

And lo, the routes unfurl before us like paths through enchanted forests:

🛣️ With each route a journey, we craft handlers to guide the way, beckoning travelers to their destinations:

import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
import '../controllers/user.controller.dart';
class UserRoutes {
Handler get handler {
final UserController _userController = UserController();
var router = Router();
router.post('/login', _userController.login);
router.post('/register', _userController.signUp);
router.get('/', _userController.getAllUsers);
router.get('/<userId>', _userController.getUserDetails);
router.put('/<userId>', _userController.updateUser);
return router;
}
}

The init.route.dart file would look like:

class InitRoute {  
Handler get handler {
var router = Router();
router.mount('/user', UserRoutes().handler); router.mount('/mortgage', MortgagegRoutes().handler);
return router;
}
}

Controller

But what of those who seek to control the flow of our kingdom’s data? Enter the controllers, stalwart defenders of order:

🛡️ The UserController, a sentinel standing watch over users and their deeds.

class UserController {  
var userRef = DBSetup().userRef;
}

In order to keep content less I’m defining only a single function

Bonus

But wait, there’s more! In the bonus realm, we uncover the secrets of file handling:

📂 StorageRoutes, gateways to realms of images, where bytes flow like rivers. 🖼️ And the ImageController, a master of uploads and retrievals, wielding bytes like paint upon a canvas.

class StorageRoutes {  
Handler get handler {
var router = Router();
final ImageController _imageController = ImageController();
router.get('/<imageId>', _imageController.getImage);
return router;
}
}

And the controller code will be:

import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:shelf/shelf.dart';
class ImageController {
uploadImage(Uint8List imageData, String fileName) async {
try {
final _uploadDirectory = Directory('storage');
if (await _uploadDirectory.exists() == false) {
await _uploadDirectory.create();
}
File file = await File('${_uploadDirectory.path}/$fileName').create();
print(await file.exists());
var res = await file.writeAsBytes(imageData);
return res.path;
} catch (e) {
return {"message": "$e"};
}
}

Future<Response> getImage(Request request, String imageId) async {
try {
File file = File('storage/$imageId');
return Response.ok(file.readAsBytesSync(),
headers: {'Content-type': 'image/jpg'});
} catch (e) {
return Response.internalServerError(body: jsonEncode({'message': "$e"}));
}
}
}

In the controller code you can fetch the imageData from the formData and you can call the uploadImage function to upload it. Or else you can create a separate route to upload it .

Conclusion

And thus, our saga comes to a close, our Dart server standing tall, a testament to the power of good practices and structured code. May it serve as a beacon of inspiration for developers far and wide, guiding them on their own epic quests in the realms of programming.

And should you wish to delve deeper into the mysteries of this tale, seek out the wise sage shashiben on the scrolls of GitHub, where knowledge flows like the rivers of code.

--

--

Shashiben

Experienced full stack developer who care deeply about building interfaces that are usable and pleasant for the most number of people.