Project: userver (App Definition and Development / Streaming & Messaging)
File: mongo_service.md

Make sure that you can compile and run core tests and read a basic example @ref scripts/docs/en/userver/tutorial/hello_service.md. In this tutorial we will write a service that stores history of translation changes and returns the most recent translations. MongoDB would be used as a database. The service would have the following Rest API: HTTP PATCH by path '/v1/translations' with query parameters 'key', 'lang' and 'value' updates a translation. HTTP GET by path '/v1/translations' with query parameter 'lastupdate' returns unique translations that were added after the 'lastupdate'. Like in @ref scripts/docs/en/userver/tutorial/hello_service.md we create a component for handling HTTP requests: @snippet samples/mongoservice/mongoservice.cpp Mongo service sample - component Note that the component holds a storages::mongo::PoolPtr - a client to the Mongo. That client is thread safe, you can use it concurrently from different threads and tasks. In the `Translations::InsertNew` function we get the request arguments and form a BSON document for insertion. @snippet samples/mongoservice/mongoservice.cpp Mongo service sample - InsertNew There are different ways to form a document, see @ref scripts/docs/en/userver/formats.md. MongoDB queries are just BSON documents. Each mongo document has an implicit `_id` field that stores the document creation time. Knowing that, we can use formats::bson::Oid::MakeMinimalFor() to find all the documents that were added after `updatetime`. Query sorts the documents by modification times (by `id`), so when the results are written into formats::json::ValueBuilder latter writes rewrite previous data for the same key. @snippet samples/mongoservice/mongoservice.cpp Mongo service sample - ReturnDiff See @ref scripts/docs/en/userver/mongodb.md for MongoDB hints and more usage samples. Static configuration of service is quite close to the configuration from @ref scripts/docs/en/userver/tutorial/hello_service.md except for the handler and DB: @snippet samples/mongoservice/staticconfig.yaml Mongo service sample - static config There are more static options for the MongoDB component configuration, all of them are described at components::Mongo. Finally, we add our component to the components::MinimalServerComponentList(), and start the server with static configuration `kStaticConfig`. @snippet samples/mongoservice/mongoservice.cpp Mongo service sample - main To build the sample, execute the following build steps at the userver root directory: ``` mkdir build_release cd build_release cmake -DCMAKEBUILDTYPE=Release .. make userver-samples-mongo_service ``` The sample could be started by running `make start-userver-samples-mongo_service`. The command would invoke @ref scripts/docs/en/userver/functional_testing.md "testsuite start target" that sets proper paths in the configuration files, prepares and starts the DB, and starts the service. To start the service manually start the DB server and run `./samples/mongoservice/userver-samples-mongoservice -c </path/to/static_config.yaml>`. Now you can send a request to your service from another terminal: ``` bash $ curl -X PATCH 'http://localhost:8090/v1/translations?key=hello&lang=ru&value=' $ curl -X PATCH 'http://localhost:8090/v1/translations?key=hello&lang=ru&value=' $ curl -X PATCH 'http://localhost:8090/v1/translations?key=hello&lang=ru&value=' $ curl -s http://localhost:8090/v1/translations?last_update=2021-11-01T12:00:00Z | jq { "content": { "hello": { "ru": "" }, "wellcome": { "ru": "" } }, "update_time": "2021-12-20T10:17:37.249767773+00:00" } ``` @ref scripts/docs/en/userver/functional_testing.md "Functional tests" for the service could be implemented using the testsuite. To do that you have to: Turn on the pytest_userver.plugins.mongo plugin and provide Mongo settings info for the testsuite: @snippet samples/mongo_service/tests/conftest.py mongodb settings The pytestuserver.plugins.serviceclient.autoclientdeps() fixture already known about the mongodb fixture, so there's no need to override the extraclientdeps() fixture. Write the test: @snippet samples/mongoservice/tests/testmongo.py Functional test See the full example: @ref samples/mongoservice/mongoservice.cpp @ref samples/mongoservice/staticconfig.yaml @ref samples/mongo_service/CMakeLists.txt @ref samples/mongo_service/tests/conftest.py @ref samples/mongoservice/tests/testmongo.py @htmlonly <div class="bottom-nav"> @endhtmlonly @ref scripts/docs/en/userver/tutorial/postgresservice.md | @ref scripts/docs/en/userver/tutorial/redisservice.md @htmlonly </div> @endhtmlonly @example samples/mongoservice/mongoservice.cpp @example samples/mongoservice/staticconfig.yaml @example samples/mongo_service/CMakeLists.txt @example samples/mongo_service/tests/conftest.py @example samples/mongoservice/tests/testmongo.py