Mon code Java range des fichiers dans S3 et des données dans DynamoDB. Comment le développer et l’essayer sur ma machine, sans compte AWS, sans facture, et sans changer le code qui parle à AWS ?

Cet article présente Floci, un émulateur AWS local, avec un exemple de bout en bout : l’AWS CLI crée les ressources, une petite application Java les utilise, et la console web de Floci les montre. Je contribue au projet Floci.

Pourquoi un émulateur local

Pour essayer du code qui parle à AWS, on peut utiliser un vrai compte : il faut des identifiants, chaque appel peut coûter, et une erreur de configuration peut toucher autre chose qu’un environnement de test. Un émulateur local répond aux mêmes appels, sur la machine, sans compte et sans facture.

LocalStack est l’outil le plus connu dans ce domaine. Depuis mars 2026, d’après son propre blog, il est livré en une seule image qui demande un jeton d’authentification (« auth token »), avec une offre gratuite liée à un compte pour les particuliers et les projets open source. Floci fonctionne sans compte et sans jeton.

Floci en bref

D’après son README, Floci est « a free, open-source local AWS emulator for development, testing, and CI », sous licence MIT. Il fait partie d’une famille d’émulateurs : floci pour AWS (port 4566), et d’autres pour Azure, GCP et OCI.

Tous les services AWS répondent sur http://localhost:4566. N’importe quelle région publiée marche, et les identifiants peuvent être n’importe quelles valeurs non vides.

Un émulateur, pas un mock

Dans un test, un mock remplace le client AWS par un faux objet. Il répond ce qu’on lui a dit de répondre. Le code ne fait aucun appel réseau, et le test vérifie surtout ce qu’on croit savoir d’AWS.

Floci est d’une autre nature : c’est un serveur qui parle le protocole d’AWS. Le code garde le vrai client AWS (celui de l’AWS CLI, du SDK Java, de Terraform…), qui envoie de vraies requêtes HTTP, signées comme pour AWS. Seule l’adresse change : localhost au lieu d’AWS.

Le README décrit son architecture :

  • un routeur HTTP reçoit les requêtes au format d’AWS ;
  • des services sans état (SQS, SNS, IAM, STS, KMS, Secrets Manager…) ;
  • des services avec état : S3, DynamoDB et DynamoDB Streams ;
  • des services qui démarrent de vrais conteneurs Docker : Lambda, RDS, ElastiCache, MSK, ECS, EC2, EKS… ;
  • un stockage au choix : en mémoire, persistant, hybride, ou avec un journal d’écriture (« write-ahead log »).

Floci mesure sa compatibilité avec des suites de tests par SDK et par outil, publiées dans son dépôt : par exemple 1 326 tests pour le SDK AWS pour Java v2, 205 pour l’AWS CLI v2, 67 pour Terraform.

Démarrer Floci

La façon la plus simple est sa CLI, floci-cli (Docker doit tourner) :

floci start
floci status

Floci tourne alors dans un conteneur, et répond sur le port 4566.

Avec l’AWS CLI

floci env affiche les variables qui envoient l’AWS CLI vers Floci :

export AWS_ENDPOINT_URL='http://localhost.floci.io:4566'
export AWS_ACCESS_KEY_ID='test'
export AWS_SECRET_ACCESS_KEY='test'
export AWS_DEFAULT_REGION='us-east-1'

localhost.floci.io est un nom qui pointe vers 127.0.0.1, comme ses sous-domaines : documents.localhost.floci.io aussi. C’est utile pour S3, qui peut mettre le nom du bucket dans le nom d’hôte.

Je crée les ressources de l’exemple : un bucket pour les textes, une table pour les informations.

eval "$(floci env)"
aws s3 mb s3://documents
aws dynamodb create-table \
  --table-name document-info \
  --attribute-definitions AttributeName=id,AttributeType=S \
  --key-schema AttributeName=id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

Pour voir que la CLI ne fait rien de spécial, l’option --debug montre la requête :

aws s3 ls --debug
Endpoint provider result: http://localhost.floci.io:4566
Sending http request: <AWSPreparedRequest ... method=GET, url=http://localhost.floci.io:4566/,
  headers={... 'Authorization': b'AWS4-HMAC-SHA256 Credential=test/..., SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=...' ...}>
Response headers: {'Content-Type': 'application/xml', ..., 'x-amz-request-id': '9f476e72-...'}

(J’ai raccourci les lignes.) La requête est signée avec SigV4 (AWS4-HMAC-SHA256), exactement comme pour AWS, et Floci répond en XML, comme S3. Pour ne pas exporter ces variables dans chaque terminal, un profil AWS nommé fait la même chose ; je l’explique dans l’article sur mon poste de dev.

Avec le SDK Java

L’exemple est une petite application, publiée sur GitLab : floci-aws-local-demo. Elle range des « documents » : le texte dans S3, les informations (titre, auteur) dans DynamoDB, et la consultation renvoie les deux ensemble. Elle utilise le SDK AWS pour Java v2 ; Quarkus n’y sert que de couche web.

EndpointCe qu’il faitService AWS
POST /documents (texte)range le texte, renvoie un idS3
POST /documents/{id}/info (JSON)range le titre et l’auteurDynamoDB
GET /documents/{id}renvoie les informations et le texteDynamoDB + S3
GET /documentsliste les documentsDynamoDB

C’est un exemple pédagogique : chaque classe crée son client avec des constantes visibles. Le client S3 (extrait simplifié de DocumentTextResource.java) :

private static final URI ENDPOINT = URI.create("http://localhost:4566");
private static final Region REGION = Region.US_EAST_1;
// Floci accepts any non-empty credentials.
private static final AwsBasicCredentials CREDENTIALS = AwsBasicCredentials.create("test", "test");

try (S3Client s3 = S3Client.builder()
        .endpointOverride(ENDPOINT)
        .region(REGION)
        .credentialsProvider(StaticCredentialsProvider.create(CREDENTIALS))
        .build()) {

    PutObjectRequest request = PutObjectRequest.builder()
            .bucket("documents")
            .key(id + ".txt")
            .contentType(MediaType.TEXT_PLAIN)
            .build();
    s3.putObject(request, RequestBody.fromString(text));
}

C’est le client S3 qu’on utiliserait avec AWS. La seule différence est endpointOverride : avec le vrai AWS, on retire cette ligne et on utilise de vrais identifiants. Le client DynamoDB se crée de la même façon, dans DocumentInfoResource.java.

L’application se lance sur la machine, avec les versions de Java et de Maven données par son fichier mise.toml :

mise install
mvn quarkus:dev

Puis on range un document, ses informations, et on le relit :

curl -X POST localhost:8080/documents -H 'Content-Type: text/plain' \
  --data 'Floci speaks the AWS protocol on port 4566.'
{"id":"3fa7b64a-10e9-4e1e-b9d3-258a043f8122"}
curl -X POST localhost:8080/documents/3fa7b64a-10e9-4e1e-b9d3-258a043f8122/info \
  -H 'Content-Type: application/json' -d '{"title":"Hello Floci","author":"Foulen Foulani"}'
curl localhost:8080/documents/3fa7b64a-10e9-4e1e-b9d3-258a043f8122
{"id":"3fa7b64a-…","title":"Hello Floci","author":"Foulen Foulani","createdAt":"2026-10-24T03:12:58.387781Z","text":"Floci speaks the AWS protocol on port 4566."}

Le titre vient de DynamoDB, le texte de S3. Un identifiant inconnu répond 404.

Ici, l’adresse de Floci est une constante dans le code. Dans un vrai projet, on peut la laisser hors du code : d’après le guide AWS, le SDK pour Java 2.x lit AWS_ENDPOINT_URL, ou endpoint_url dans un profil AWS (le SDK 1.x, non).

La console web

Floci a une console web : http://localhost:4566/_floci/ui. Elle ne tourne pas au démarrage. À la première visite, Floci lance un conteneur à côté de lui, qui contient la console, puis y redirige le navigateur (ici, vers le port 4500). C’est pour cela que Floci a besoin du socket Docker (floci start s’en charge). La console ne fait pas que montrer : on peut aussi y créer un bucket, envoyer un fichier, ajouter un élément dans une table ou lancer une requête PartiQL.

La page d’accueil de la console web de Floci : les services AWS disponibles et le nombre de ressources de chacun.

Le bucket documents contient le texte rangé par l’application :

Dans la console de Floci, le bucket documents : ses informations à droite, et en bas son fichier 3fa7b64a-….txt de 43 octets.

Et la table document-info contient ses informations. Une requête PartiQL les affiche en JSON :

Dans la console de Floci, la table document-info avec l’élément de Foulen Foulani, et le résultat de la requête PartiQL SELECT * FROM document-info : createdAt, author, id et title.

Ce qu’il faut savoir

Un émulateur n’est pas AWS. Floci reproduit le protocole et le comportement des services, et ses suites de tests mesurent cette compatibilité ; mais pour un service précis, il vaut mieux vérifier sa place dans la liste des services et dans ces tests. Les services qui démarrent de vrais conteneurs (Lambda, RDS…) demandent Docker.

Pour aller plus loin, Floci a aussi des modules Testcontainers (Java, Node.js, Python, .NET) qui démarrent un Floci isolé pour chaque test. Tester son code avec Floci fera l’objet d’un prochain article.

Testé sur macOS avec Floci 2.3.0 (CLI 0.4.0), l’AWS CLI 2.38.5, Java 21 (Temurin 21.0.13), Quarkus 3.40.1.1 et le SDK AWS pour Java 2.46.7.

Sources : le README de Floci (présentation, architecture, console web, tests de compatibilité, Testcontainers), floci-cli, le billet de LocalStack sur la livraison de ses émulateurs, et le guide AWS sur les endpoints.

Pour aller plus loin