Ugyanazt a webes API-t építettem át az Express, a Flask és az ASP.NET használatával. Itt találtam.

Már vásároltam egy háttérrendszert, hogy támogassak egy asztali játékalkalmazást, és úgy döntöttem, hogy elvégzek egy kis kutatást, hogy meghatározzam az igényeimnek leginkább megfelelőt.

A cél egyértelmű volt: létrehozni egy egyszerű RESTful API-t, amely lehetővé teszi az elülső alkalmazások számára, hogy alapvető CRUD műveleteket hajtsanak végre, bemutatva nekem, hogy nézzen ki a fejlesztési folyamat.

Nagyon sok háttér-keret opció létezik, és a JavaScriptet, a C # -ot és a Python-t ismerem leginkább (ebben a sorrendben), amelyek némileg korlátozták a lehetőségeimet. A természetes kiindulópont egy egyszerű kezelőfelület létrehozása volt, amely kéréseket küld egy API-nak, amely viszont egy helyi adatbázisból olvas és ír.

Fejlesztési folyamatomat az Express-szel kezdtem, amely hamarosan megmagyarázandó okokból arra késztetett, hogy megnézzem a Flask-ot és az ASP.NET-et is. Úgy gondoltam, hogy eredményeim hasznosak lehetnek mások számára, akik kis projektek hátterének kereteit kutatják. Ebben a cikkben kód példákat és forrásokat is közlök, amelyekkel mindent felépítettem.

A teljes kódot a GitHubon is elérheti.

Figyelmeztetnem kell, hogy nem fogom egyik keretrendszert népszerűsíteni a másikon, és még nem hasonlítottam össze a telepítést, a hitelesítést vagy a skálázhatóságot. A futásteljesítmény változhat, ha ezek az adatok fontosak az Ön számára!

Azonban alul adok egy TL; DR-t , ha csak az összefoglalót és a legfontosabb tanulságokat szeretné megkapni.

Essünk neki!

Az API meghatározása

Ha még nem ismeri a webfejlesztést, akkor azt kérdezheti: "mi az API?"

Százszor kellett feltennem a kérdést, hogy találjak értelmes választ. És ez tényleg nem volt, amíg nem épül a saját, hogy azt mondhatnám, megértettem, mi az az API tesz .

Leegyszerűsítve: az API vagy az "alkalmazás-programozási felület" lehetővé teszi két különböző számítástechnikai rendszer közötti beszélgetést. Ebben a cikkben egy egyszerű kezelőfelületet mutatok be, amely megjelenít egy "küldetés" nyomkövetőt, amelyet a játékosok megtekinthetnek az asztali szerepjátékhoz. Minden küldetésnek van egy "neve" és egy "leírása", mindkettő megjelenik a webböngészőben.

Ha már megvannak az összes küldetésem a weboldalon, és csak azt szeretném, ha a játékosok megnéznék őket, akkor nem lenne szükségem API-ra vagy háttérprogramra. Ehhez a projekthez azonban azt szeretném, hogy lehetővé tegyem a felhasználók számára, hogy küldetéseket adhassanak hozzá, keressenek rájuk, töröljék őket stb. Ezekhez a műveletekhez valahol tárolnom kell a küldetéseket, de a kezelői alkalmazásom nem képes közvetlenül az adatbázisba továbbítani az információkat.

Ehhez szükségem van egy olyan API-ra, amely képes HTTP-kéréseket fogadni a webhelyről, kitalálni, mit kell kezdeni ezekkel a kérésekkel, kölcsönhatásba lépni az adatbázisommal, és további információkat küldeni a lánc mentén, hogy a felhasználó láthassa, mi történt.

Az egészet - a kezelőfelület "kliense", a háttér "API" vagy kiszolgálója és az adatbázis - "veremnek", pontosabban a "teljes veremnek" nevezzük. Ehhez a projekthez egy egyszerű kezelőfelületet építettem a verem tetejére, és mindent alatta kapcsoltam ki, amikor kipróbáltam a különböző kereteket és adatbázisokat.

Projekt felépítése

A projekt felépítése meglehetősen egyszerű volt, a kezelőfelületet három különböző szervertől elválasztva, amelyeket az API kiszolgálásához szükség szerint felpörgetnék.

A Visual Studio Community-t használtam kódszerkesztőként és IDE-ként, a szükséges nyelvi csomagokkal a JavaScript, a Python és a C # számára telepítve.

Sorra áttekintést adok az egyes keretrendszerekkel kapcsolatos tapasztalataimról, hivatkozásokkal az oktatóanyagokhoz és a csomagokhoz, amelyeket arra használtam, hogy működjenek együtt az ügyféllel. De először vessünk egy pillantást a kezelőfelületre!

Az ügyfél: Vue.js

A kliens célja az volt, hogy legyen egy egyszerű weboldala, amely információt kap az adatbázisból az API-n keresztül, és megjeleníti a felhasználó számára. A folyamat egyszerűsítése érdekében az volt a követelményem, hogy a kliensnek csak az adatbázis összes elemét kell "elolvasnia", és a felhasználó számára lehetővé kell tennie egy új küldetés "létrehozását".  

Ezek az "olvasás" és a "létrehozás" műveletek - az "R" és a "C" a "CRUD" -ben - analógak a "GET" és a "POST" HTTP módszerekkel, amelyeket az alábbi kódban láthatunk.

A kezelőfelület fejlesztésében a legkényelmesebb a Vue használata, és a Vue parancssori felületét egy alap kliens állványozására használtam a következő fájlszerkezettel:  

A Vue CLI által biztosított kazántábla jelölést a következőkre cseréltem:

RPG Quests

{{quest.name}}: {{quest.description}}

Add Quest

És a megfelelő Vue-kód:

import axios from 'axios'; export default { name: 'App', data: function () { return { quests: null, newQuestName: null, newQuestDescription: null } }, methods: { getQuests: function () { axios .get('//localhost:3000/quests') .then(response => (this.quests = response.data)); }, addQuest: function () { axios .post('//localhost:3000/quests', { name: this.newQuestName, description: this.newQuestDescription }); }, postQuest: function () { axios.all([this.addQuest(), this.getQuests()]); this.$forceUpdate(); } }, mounted: function () { this.getQuests(); } }

Ha még nem ismeri a Vue-t, a kezelőfelület sajátosságai nem annyira fontosak! Jelentős itt az, hogy egy Axios nevű JavaScript-csomagot használok GET és POST kéréseim elküldéséhez egy potenciális szerverhez.

Amikor az ügyfél betöltődik, GET-kérést küld az URL // localhost: 3000 / quests címre, hogy az összes küldetést betöltse az adatbázisból. Ezenkívül tartalmaz néhány beviteli mezőt és egy gombot, amely új küldetést tesz közzé.

A Vue CLI használatával az ügyfél kiszolgálása a // localhost: 8080 webhelyen, az alkalmazás elülső része így néz ki:

Miután a küldetéseket hozzáadta az adatbázishoz, azok megjelennek az "RPG küldetések" fejléc és a beviteli mezők között.

Ügyfélforrások

Az ügyfél felépítéséhez a következőket használtam:

  • NodeJS / NPM a csomagkezeléshez
  • Vue CLI állványozáshoz, kiszolgáláshoz és építési projektekhez
  • Axios HTTP-kérelmek elküldéséhez az API-hoz
  • Vue Axios dokumentáció az Axios API-val való együttműködésének értelmezéséhez
  • Postás az API kérelmek böngészőn keresztüli teszteléséhez, mielőtt azokat végrehajtaná az ügyfélben.

JavaScript API: Express

Az Express egy könnyű webkeret a NodeJS számára, amely lehetővé teszi, hogy szerveroldali alkalmazásokat írjon JavaScript segítségével.

It's un-opinionated, which means that you can build your applications how you like without it defining the architecture for you. You can add packages to improve functionality as you fancy, which I found to be a double-edged sword as a newbie to the framework. More on that later.

Being most comfortable in JavaScript, I was excited by the prospect of having the entire stack run on just one language instead of several. I had heard of the "MEVN Stack," which denotes a full stack application that is comprised of MongoDB, Express, Vue, and NodeJS, and decided to try that out for this iteration of the project.

I followed a web API tutorial to first build a template app, then used another MEVN tutorial to fill in the details of how to get the API to communicate with the Vue client that I had built. The Express API that I created for this project follows a similar structure to the former, using MongoDB as the database:

If you're coming from a JavaScript background, Express is fairly easy to read, even if you're not familiar with some of the back end terminology. The following is a snippet from /routes/quests.js, for example, which handles the HTTP endpoint requests:

router.get('/', async (req, res) => { try { const quests = await Quest.find(); res.json(quests); } catch (err) { res.status(500).json({ message: err.message }); } }); router.post('/', async (req, res) => { const quest = new Quest({ name: req.body.name, description: req.body.description }); try { const newQuest = await quest.save(); res.status(201).json(newQuest); } catch (err) { res.status(400).json({ message: err.message }); } });

The general theme of the code is to receive a request, attempt to contact the database to do work, and then send a response back to whoever's asking. The specifics can be quite complex, particularly if you're writing your own middleware that does things in between the request and response, but the code is at least readable.

I found MongoDB to be painless to work with as a NoSQL database.  If you're working with Express, you'll most likely use Mongoose as an ODM - basically like a "middle person" that translates a model of what your data looks like to the database.

The model in this app (called a "schema" in Mongoose terms) is really simple, located in /models/quests.js:

const questSchema = new mongoose.Schema({ name: { type: String, required: true }, description: { type: String, required: true } });

The above indicates that the database should store our two fields: a quest name and a quest description.  Both of these fields are strings, and required. All GET and POST requests will have to conform to this model to interact with the database.

After wiring all of this up and POSTing a few new quests, the front end site started populating with data:

The process of setting up the Express API was not without its hair pulling, however. Being a primarily front end and 2D game developer, I've become intimately familiar with how dispersed the JavaScript ecosystem can feel. This frustration was magnified in attempting to build a back end app. There are a lot of packages required to get everything up and running, each of which having its own required configuration and implementation.

If you're looking for a framework that just does everything out of the box, Express is most certainly not the choice for you. It's lightweight, flexible, and easy to read, in a very "choose-your-own-adventure" fashion. I quite like how clean the code is and the ability to structure my projects as I see fit, but troubleshooting and error handling do leave a lot to be desired.

JavaScript/Express Resources

To build the JavaScript API, I used:

  • NodeJS/NPM for package management
  • Express as the main web framework
  • Dotenv to create environment-specific variables
  • Nodemon to watch files for changes and restart the server so I didn't have to
  • CORS to allow for cross-origin requests (basically a pain if you're trying to make requests from a client to a server that are both running locally on your machine)
  • MongoDB for the NoSQL database
  • Mongoose for writing models that map onto MongoDB
  • This API tutorial to provide a basic understanding of how to create an Express-MongoDB stack
  • This MEVN tutorial to fill in the gaps of running a MongoDB-Express-Vue-Node full stack

Python API: Flask

In the process of building the Express API, I had a conversation with a data science friend who works in Python. This gave me the idea of trying out non-JavaScript frameworks to see if they were better suited for my app.

I took a cursory look at Django, since I'd been hearing about it as a powerhouse back end framework that provides everything out of the box. I was a little intimidated by how opinionated it seemed, and opted to try out Flask instead, which kind of felt like the Python equivalent of Express.

I followed the first few bits of the excellent Flask Mega-Tutorial to get my app structure set up, using the companion RESTful API tutorial to fill in the pieces of HTTP requests. The file structure turned out to be only a shade more complex than that of the Express API:

The tutorial I followed uses SQLite for its database, with Flask-SQLAlchemy as an ORM. The HTTP request code that's most analogous to the Express API is located in /app/routes.py:

@app.route('/quests', methods=['GET']) def get_quests(): questQuery = Quest.query.all() quests = {} for quest in questQuery: quests[quest.name] = quest.description return jsonify(quests) @app.route('/quests', methods=['POST']) def post_quest(): newQuest = Quest(name=request.json['name'], description=request.json['description']) db.session.add(newQuest) db.session.commit() return "Quest Added!"

Similarly, the database model (akin to the Mongoose "schema") is in /app/models.py:

class Quest(db.Model): name = db.Column(db.String(256), primary_key=True, index=True, unique=True) description = db.Column(db.String(256), index=True, unique=True)

As I mentioned, I'm more familiar with JavaScript and C# than with Python, and working with the latter to build the Flask API felt like cheating. Certain things like pathing, package handling, and writing workable code were just easy, although I did get hung up on getting the API to correctly parse JSON for the client. I suspect that was more of an issue of my unfamiliarity with the language than anything else, but it did take time to troubleshoot.

To be quite honest, coming from a non-Flask background, I did kind of expect to complete a couple of tutorials and spin up an API without having to do all that much work for it.  

I can't say that it turned out that way, as Python does have its own particulars that require some time to get used to. Still, the Python ecosystem appears to be extremely well organized, and I enjoyed my time building the Flask API.

I've also heard that Django is a better and more scalable option for larger projects. But it seems like it would involve a separate, and steeper, learning curve to become proficient.

Flask was easy enough for me as a non-Python developer to pick up and build something over a weekend. I suspect that learning Django would take quite a bit longer, but with potentially greater dividends over the long run.

Python/Flask Resources

To build the Flask API, I used:

  • Python 3/pip for package management
  • Flask as the main web framework
  • python-dotenv to configure environment variables
  • SQLite as the database
  • Flask-SQLAlchemy as the ORM to work with SQLite
  • Flask-Migrate as an additional tool to migrate data to SQLite
  • Flask-CORS to handle the same CORS issue as with the Express API
  • The Flask Mega-Tutorial to learn the basics
  • The Flask REST API tutorial to understand how to receive HTTP requests

C# API: ASP.NET

I can't tell you how many times I've Googled ".NET" to understand what it is, how it's different from ASP.NET, and why I'd want to use any of it. My C# knowledge comes mainly from working with Unity, which exists somewhat adjacent to .NET and doesn't provide for a lot of exposure to Microsoft's larger ecosystem.

I've spent some time researching Razor Pages and MVC, and finally came to understand ASP.NET's breadth of features as Microsoft's open source web framework. I decided to toss ASP.NET into the hat for a potential back end for my app, and set about working through the official web API tutorial with ASP.NET Core and MongoDB.

The file structure for this version of the API was more complex than the others, given that .NET projects tend to have a much larger footprint:

I should also mention that I already had Visual Studio and all of the required workloads installed, which made the setup process easier. Plus, having spent time with MongoDB for the Express API, I found the database portion of the project to be similar, although by default, ASP.NET seems to prefer using Microsoft's SQL Server and the Entity Framework ORM.

The ASP.NET code for HTTP requests is a bit more complex than what we've seen with the two other APIs, but it's no match for all of the code that sits around it.  

First, consider this snippet in /Controllers/QuestController.cs that handles requests:

namespace QuestAPI.Controllers { [Route("quests/")] [ApiController] public class QuestsController : ControllerBase { private readonly QuestService _questService; public QuestsController(QuestService questService) { _questService = questService; } [HttpGet] public ActionResult
    
      Get() => _questService.Get(); [HttpPost] public ActionResult Create(Quest quest) { _questService.Create(quest); return CreatedAtRoute("GetQuest", new { id = quest.Id.ToString() }, quest); } } }
    

Not too terrible, almost kind of readable, in a C# sort of way. The data model in /Models/Quest.cs is even easier:

namespace QuestAPI.Models{ public class Quest { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } [BsonElement("Name")] public string Name { get; set; } public string Description { get; set; } } }

These two snippets essentially do the same things as the previous examples that we've seen: take requests from the front end, process them to get or modify data in the database, and send a response back to the client.  

Yet, as you can probably tell from the complex file structure, there's so much code that surrounds these snippets, along with Interfaces, Dependency Injection, and other abstractions, that it can be challenging to understand how it all works together.

Consider the following configuration code in /Startup.cs:

 public void ConfigureServices(IServiceCollection services) { services.Configure(Configuration.GetSection(nameof(QuestDatabaseSettings))); services.AddSingleton(sp => sp.GetRequiredService
    
     ().Value); services.AddSingleton(); services.AddCors(options => { options.AddPolicy(MyAllowSpecificOrigins, builder => { builder.WithOrigins("//localhost:3000/quests", "//localhost:8080").AllowAnyHeader().AllowAnyMethod(); }); }); services.AddControllers(); }
    

Or this particularly nested bit from a separate SQL Server web API tutorial:

 [HttpGet] public async Task
    
     > GetTodoItems() { return await _context.TodoItems .Select(x => ItemToDTO(x)) .ToListAsync(); }
    

Lol. What?? As a new user, even familiar as I am with C#, I can go line-by-line to understand each abstraction, or I can just trust that the framework is handling everything for me and forget about it.

I tend to want to know exactly how my code works so that I can fix or alter it if necessary. But I certainly feel like my time spent learning the ins-and-outs of ASP.NET could be better utilized towards mastering another framework.

To be fair, ASP.NET appears to be similar to Django in being more opinionated and providing you with a ton of stuff out of the box, including an authentication solution, database management, and the lot. If these things are important to you, it's certainly worth considering.  

It also has the full support of Microsoft and an open source community. So if you're looking at developing enterprise-level applications that need to scale, you might want to take a longer look at ASP.NET as a potential solution.

C#/ASP.Net Resources

To build the ASP.Net API, I used the following resources:

  • Visual Studio Community as my code editor and IDE, with the ASP.NET and web development workload installed (I already had MongoDB running from the Express API)
  • Microsoft's official tutorial for building web APIs with ASP.NET and MongoDB

TL;DR

In all, with some slight variations and hiccups among them, I've gotten each of the web APIs to work with the Vue client, with the ability to view quests from and add quests to the database. Hopefully, my explanation of the process has been helpful in your own search for a back end framework, but here are some additional recommendations just in case:

  • If you're a JavaScript developer and/or want to manage everything that your application does, including its architecture, consider using Express.
  • If you're a Python developer and/or want a pleasant experience in developing small projects, try Flask, but consider using Django if you need more out-of-the-box support and don't mind conforming to an opinionated framework.
  • If you're a C# developer and willing to spend the time to learn the most arcane details of C# coding best practices, consider using ASP.NET. Alternatively, if you need enterprise-level support right out of the box, you'd be hard-pressed to find better.
  • If you don't know what to use and just want to learn back end development, take a look at Flask.  It's easy to work with and will teach you the basics that you'll need to know for building web apps in any coding language.
  • If you don't know what to use and want an adventure, choose Express. There's a rabbit hole of package management and Stack Overflow questions waiting that may make you tear your hair out, but you'll learn a lot about the JavaScript ecosystem and web development in general.

Additionally, two things bear mentioning that threw me for a spin in this process: CORS and environment variables. The former I've mentioned in this article a couple of times already, but it's worth discussing again to understand the scope of building a full stack app on your machine.

Unless you have an integrated development environment that's handling the whole stack for you, you'll likely have a client, a server, and a database that are all running independently of one another.  

In the Express API section above, for example, I was running

  1. the Vue CLI server, which rendered my front end app on port 8080;
  2. an NPM script to spin up the Express API server on port 3000; and
  3. a separate instance of the Mongo database to get everything working together. That's three command prompts open and a general mess!

If you dig into the Vue code above (or on GitHub), you'll see that the requests made on behalf of the client, running on //localhost:8080, are to the server on //localhost:3000, which is where the Express API is listening. This is called "cross-origin resource sharing," or CORS, and it's blocked by the browser for security concerns. Most frameworks require you to install an additional package to get the whole thing running in your local environment.

Second, you'll want to become comfortable with environment variables, which can really help smooth some rough pathing edges at runtime. I used dotenv and Flask-Env for the Express and Flask projects, respectively.

Both packages allow you to configure things like where your database lives, or what default port your application should be using, in one document. Your application then uses that document at runtime to figure out where to find everything, without any further configuration needed from you.

One final note that may be helpful if you're just working on a back end project and don't want to go through the trouble of building a front end client: consider using a third-party app like Postman. I used it to make HTTP requests to each of the APIs to make sure they were working properly before layering on the Vue client and trying to get the whole stack running altogether.

I hope this article has been helpful for you in your own process of looking for a back end framework.  Let me know what you find!

If you enjoyed this article, please consider checking out my games and books, subscribing to my YouTube channel, or joining the Entromancy Discord.

M. S. Farzan, Ph.D. has written and worked for high-profile video game companies and editorial websites such as Electronic Arts, Perfect World Entertainment, Modus Games, and MMORPG.com, and has served as the Community Manager for games like Dungeons & Dragons Neverwinter and Mass Effect: Andromeda. He is the Creative Director and Lead Game Designer of Entromancy: A Cyberpunk Fantasy RPG and author of The Nightpath Trilogy. Find M. S. Farzan on Twitter @sominator.